Changeset View
Changeset View
Standalone View
Standalone View
sys/net/pfil.c
Show First 20 Lines • Show All 64 Lines • ▼ Show 20 Lines | |||||
static struct mtx pfil_lock; | static struct mtx pfil_lock; | ||||
MTX_SYSINIT(pfil_mtxinit, &pfil_lock, "pfil(9) lock", MTX_DEF); | MTX_SYSINIT(pfil_mtxinit, &pfil_lock, "pfil(9) lock", MTX_DEF); | ||||
#define PFIL_LOCK() mtx_lock(&pfil_lock) | #define PFIL_LOCK() mtx_lock(&pfil_lock) | ||||
#define PFIL_UNLOCK() mtx_unlock(&pfil_lock) | #define PFIL_UNLOCK() mtx_unlock(&pfil_lock) | ||||
#define PFIL_LOCK_ASSERT() mtx_assert(&pfil_lock, MA_OWNED) | #define PFIL_LOCK_ASSERT() mtx_assert(&pfil_lock, MA_OWNED) | ||||
struct pfil_hook { | struct pfil_hook { | ||||
pfil_func_t hook_func; | pfil_mbuf_chk_t hook_mbuf_chk; | ||||
pfil_mem_chk_t hook_mem_chk; | |||||
void *hook_ruleset; | void *hook_ruleset; | ||||
int hook_flags; | int hook_flags; | ||||
int hook_links; | int hook_links; | ||||
enum pfil_types hook_type; | enum pfil_types hook_type; | ||||
const char *hook_modname; | const char *hook_modname; | ||||
const char *hook_rulname; | const char *hook_rulname; | ||||
LIST_ENTRY(pfil_hook) hook_list; | LIST_ENTRY(pfil_hook) hook_list; | ||||
}; | }; | ||||
struct pfil_link { | struct pfil_link { | ||||
CK_STAILQ_ENTRY(pfil_link) link_chain; | CK_STAILQ_ENTRY(pfil_link) link_chain; | ||||
pfil_func_t link_func; | pfil_mbuf_chk_t link_mbuf_chk; | ||||
pfil_mem_chk_t link_mem_chk; | |||||
void *link_ruleset; | void *link_ruleset; | ||||
int link_flags; | int link_flags; | ||||
struct pfil_hook *link_hook; | struct pfil_hook *link_hook; | ||||
struct epoch_context link_epoch_ctx; | struct epoch_context link_epoch_ctx; | ||||
}; | }; | ||||
typedef CK_STAILQ_HEAD(pfil_chain, pfil_link) pfil_chain_t; | typedef CK_STAILQ_HEAD(pfil_chain, pfil_link) pfil_chain_t; | ||||
struct pfil_head { | struct pfil_head { | ||||
Show All 15 Lines | |||||
LIST_HEAD(pfilhookhead, pfil_hook); | LIST_HEAD(pfilhookhead, pfil_hook); | ||||
VNET_DEFINE_STATIC(struct pfilhookhead, pfil_hook_list) = | VNET_DEFINE_STATIC(struct pfilhookhead, pfil_hook_list) = | ||||
LIST_HEAD_INITIALIZER(pfil_hook_list); | LIST_HEAD_INITIALIZER(pfil_hook_list); | ||||
#define V_pfil_hook_list VNET(pfil_hook_list) | #define V_pfil_hook_list VNET(pfil_hook_list) | ||||
static struct pfil_link *pfil_link_remove(pfil_chain_t *, pfil_hook_t ); | static struct pfil_link *pfil_link_remove(pfil_chain_t *, pfil_hook_t ); | ||||
static void pfil_link_free(epoch_context_t); | static void pfil_link_free(epoch_context_t); | ||||
int | /* | ||||
pfil_realloc(pfil_packet_t *p, int flags, struct ifnet *ifp) | * To couple a filtering point that provides memory pointer with a filter that | ||||
{ | * works on mbufs only. | ||||
struct mbuf *m; | */ | ||||
MPASS(flags & PFIL_MEMPTR); | |||||
if ((m = m_devget(p->mem, PFIL_LENGTH(flags), 0, ifp, NULL)) == NULL) | |||||
return (ENOMEM); | |||||
*p = pfil_packet_align(*p); | |||||
*p->m = m; | |||||
return (0); | |||||
} | |||||
static __noinline int | static __noinline int | ||||
pfil_fake_mbuf(pfil_func_t func, pfil_packet_t *p, struct ifnet *ifp, int flags, | pfil_fake_mbuf(pfil_mbuf_chk_t func, void *mem, u_int len, struct ifnet *ifp, | ||||
void *ruleset, struct inpcb *inp) | int flags, void *ruleset, struct mbuf **mp) | ||||
{ | { | ||||
struct mbuf m, *mp; | struct mbuf m; | ||||
pfil_return_t rv; | pfil_return_t rv; | ||||
(void)m_init(&m, M_NOWAIT, MT_DATA, M_NOFREE | M_PKTHDR); | (void)m_init(&m, M_NOWAIT, MT_DATA, M_NOFREE | M_PKTHDR); | ||||
m_extadd(&m, p->mem, PFIL_LENGTH(flags), NULL, NULL, NULL, 0, | m_extadd(&m, mem, len, NULL, NULL, NULL, 0, EXT_RXRING); | ||||
EXT_RXRING); | m.m_len = m.m_pkthdr.len = len; | ||||
m.m_len = m.m_pkthdr.len = PFIL_LENGTH(flags); | *mp = &m; | ||||
mp = &m; | |||||
flags &= ~(PFIL_MEMPTR | PFIL_LENMASK); | |||||
rv = func(&mp, ifp, flags, ruleset, inp); | rv = func(mp, ifp, flags, ruleset, NULL); | ||||
if (rv == PFIL_PASS && mp != &m) { | if (rv == PFIL_PASS && *mp != &m) { | ||||
/* | /* | ||||
* Firewalls that need pfil_fake_mbuf() most likely don't | * Firewalls that need pfil_fake_mbuf() most likely don't | ||||
* know they need return PFIL_REALLOCED. | * know they need return PFIL_REALLOCED. | ||||
*/ | */ | ||||
rv = PFIL_REALLOCED; | rv = PFIL_REALLOCED; | ||||
*p = pfil_packet_align(*p); | |||||
*p->m = mp; | |||||
} | } | ||||
return (rv); | return (rv); | ||||
} | } | ||||
/* | static __always_inline int | ||||
* pfil_run_hooks() runs the specified packet filter hook chain. | pfil_mem_common(pfil_chain_t *pch, void *mem, u_int len, int flags, | ||||
*/ | struct ifnet *ifp, struct mbuf **m) | ||||
int | |||||
pfil_run_hooks(struct pfil_head *head, pfil_packet_t p, struct ifnet *ifp, | |||||
int flags, struct inpcb *inp) | |||||
{ | { | ||||
pfil_chain_t *pch; | |||||
struct pfil_link *link; | struct pfil_link *link; | ||||
pfil_return_t rv; | pfil_return_t rv; | ||||
bool realloc = false; | bool realloc = false; | ||||
NET_EPOCH_ASSERT(); | NET_EPOCH_ASSERT(); | ||||
KASSERT(flags == PFIL_IN || flags == PFIL_OUT, | |||||
("%s: unsupported flags %d", __func__, flags)); | |||||
if (PFIL_DIR(flags) == PFIL_IN) | |||||
pch = &head->head_in; | |||||
else if (__predict_true(PFIL_DIR(flags) == PFIL_OUT)) | |||||
pch = &head->head_out; | |||||
else | |||||
panic("%s: bogus flags %d", __func__, flags); | |||||
rv = PFIL_PASS; | rv = PFIL_PASS; | ||||
CK_STAILQ_FOREACH(link, pch, link_chain) { | CK_STAILQ_FOREACH(link, pch, link_chain) { | ||||
if ((flags & PFIL_MEMPTR) && !(link->link_flags & PFIL_MEMPTR)) | if (__predict_true(link->link_mem_chk != NULL && !realloc)) | ||||
rv = pfil_fake_mbuf(link->link_func, &p, ifp, flags, | rv = link->link_mem_chk(mem, len, flags, ifp, | ||||
link->link_ruleset, inp); | link->link_ruleset, m); | ||||
else if (!realloc) | |||||
rv = pfil_fake_mbuf(link->link_mbuf_chk, mem, len, ifp, | |||||
flags, link->link_ruleset, m); | |||||
else | else | ||||
rv = (*link->link_func)(p, ifp, flags, | rv = link->link_mbuf_chk(m, ifp, flags, | ||||
link->link_ruleset, inp); | link->link_ruleset, NULL); | ||||
if (rv == PFIL_DROPPED || rv == PFIL_CONSUMED) | if (rv == PFIL_DROPPED || rv == PFIL_CONSUMED) | ||||
break; | break; | ||||
else if (rv == PFIL_REALLOCED) { | else if (rv == PFIL_REALLOCED) | ||||
flags &= ~(PFIL_MEMPTR | PFIL_LENMASK); | |||||
realloc = true; | realloc = true; | ||||
} | } | ||||
} | |||||
if (realloc && rv == PFIL_PASS) | if (realloc && rv == PFIL_PASS) | ||||
rv = PFIL_REALLOCED; | rv = PFIL_REALLOCED; | ||||
return (rv); | return (rv); | ||||
} | } | ||||
int | |||||
pfil_mem_in(struct pfil_head *head, void *mem, u_int len, struct ifnet *ifp, | |||||
struct mbuf **m) | |||||
{ | |||||
return (pfil_mem_common(&head->head_in, mem, len, PFIL_IN, ifp, m)); | |||||
} | |||||
int | |||||
pfil_mem_out(struct pfil_head *head, void *mem, u_int len, struct ifnet *ifp, | |||||
struct mbuf **m) | |||||
{ | |||||
return (pfil_mem_common(&head->head_out, mem, len, PFIL_OUT, ifp, m)); | |||||
} | |||||
static __always_inline int | static __always_inline int | ||||
pfil_mbuf_common(pfil_chain_t *pch, pfil_packet_t p, struct ifnet *ifp, | pfil_mbuf_common(pfil_chain_t *pch, struct mbuf **m, struct ifnet *ifp, | ||||
int flags, struct inpcb *inp) | int flags, struct inpcb *inp) | ||||
{ | { | ||||
struct pfil_link *link; | struct pfil_link *link; | ||||
pfil_return_t rv; | pfil_return_t rv; | ||||
NET_EPOCH_ASSERT(); | NET_EPOCH_ASSERT(); | ||||
KASSERT(flags == PFIL_IN || flags == PFIL_OUT, | KASSERT(flags == PFIL_IN || flags == PFIL_OUT, | ||||
("%s: unsupported flags %d", __func__, flags)); | ("%s: unsupported flags %d", __func__, flags)); | ||||
rv = PFIL_PASS; | rv = PFIL_PASS; | ||||
CK_STAILQ_FOREACH(link, pch, link_chain) { | CK_STAILQ_FOREACH(link, pch, link_chain) { | ||||
rv = (*link->link_func)(p, ifp, flags, link->link_ruleset, inp); | rv = link->link_mbuf_chk(m, ifp, flags, link->link_ruleset, | ||||
inp); | |||||
if (rv == PFIL_DROPPED || rv == PFIL_CONSUMED) | if (rv == PFIL_DROPPED || rv == PFIL_CONSUMED) | ||||
break; | break; | ||||
} | } | ||||
return (rv); | return (rv); | ||||
} | } | ||||
int | int | ||||
pfil_mbuf_in(struct pfil_head *head, pfil_packet_t p, struct ifnet *ifp, | pfil_mbuf_in(struct pfil_head *head, struct mbuf **m, struct ifnet *ifp, | ||||
struct inpcb *inp) | struct inpcb *inp) | ||||
{ | { | ||||
return (pfil_mbuf_common(&head->head_in, p, ifp, PFIL_IN, inp)); | return (pfil_mbuf_common(&head->head_in, m, ifp, PFIL_IN, inp)); | ||||
} | } | ||||
int | int | ||||
pfil_mbuf_out(struct pfil_head *head, pfil_packet_t p, struct ifnet *ifp, | pfil_mbuf_out(struct pfil_head *head, struct mbuf **m, struct ifnet *ifp, | ||||
struct inpcb *inp) | struct inpcb *inp) | ||||
{ | { | ||||
return (pfil_mbuf_common(&head->head_out, p, ifp, PFIL_OUT, inp)); | return (pfil_mbuf_common(&head->head_out, m, ifp, PFIL_OUT, inp)); | ||||
} | } | ||||
mjg: stray newline? | |||||
/* | /* | ||||
* pfil_head_register() registers a pfil_head with the packet filter hook | * pfil_head_register() registers a pfil_head with the packet filter hook | ||||
* mechanism. | * mechanism. | ||||
*/ | */ | ||||
pfil_head_t | pfil_head_t | ||||
pfil_head_register(struct pfil_head_args *pa) | pfil_head_register(struct pfil_head_args *pa) | ||||
{ | { | ||||
struct pfil_head *head, *list; | struct pfil_head *head, *list; | ||||
▲ Show 20 Lines • Show All 48 Lines • ▼ Show 20 Lines | |||||
pfil_hook_t | pfil_hook_t | ||||
pfil_add_hook(struct pfil_hook_args *pa) | pfil_add_hook(struct pfil_hook_args *pa) | ||||
{ | { | ||||
struct pfil_hook *hook, *list; | struct pfil_hook *hook, *list; | ||||
MPASS(pa->pa_version == PFIL_VERSION); | MPASS(pa->pa_version == PFIL_VERSION); | ||||
hook = malloc(sizeof(struct pfil_hook), M_PFIL, M_WAITOK | M_ZERO); | hook = malloc(sizeof(struct pfil_hook), M_PFIL, M_WAITOK | M_ZERO); | ||||
hook->hook_func = pa->pa_func; | hook->hook_mbuf_chk = pa->pa_mbuf_chk; | ||||
hook->hook_mem_chk = pa->pa_mem_chk; | |||||
hook->hook_ruleset = pa->pa_ruleset; | hook->hook_ruleset = pa->pa_ruleset; | ||||
hook->hook_flags = pa->pa_flags; | hook->hook_flags = pa->pa_flags; | ||||
hook->hook_type = pa->pa_type; | hook->hook_type = pa->pa_type; | ||||
hook->hook_modname = pa->pa_modname; | hook->hook_modname = pa->pa_modname; | ||||
hook->hook_rulname = pa->pa_rulname; | hook->hook_rulname = pa->pa_rulname; | ||||
PFIL_LOCK(); | PFIL_LOCK(); | ||||
LIST_FOREACH(list, &V_pfil_hook_list, hook_list) | LIST_FOREACH(list, &V_pfil_hook_list, hook_list) | ||||
▲ Show 20 Lines • Show All 101 Lines • ▼ Show 20 Lines | if (pa->pa_flags & PFIL_OUT) | ||||
CK_STAILQ_FOREACH(link, &head->head_out, link_chain) | CK_STAILQ_FOREACH(link, &head->head_out, link_chain) | ||||
if (link->link_hook == hook) { | if (link->link_hook == hook) { | ||||
error = EEXIST; | error = EEXIST; | ||||
goto fail; | goto fail; | ||||
} | } | ||||
if (pa->pa_flags & PFIL_IN) { | if (pa->pa_flags & PFIL_IN) { | ||||
in->link_hook = hook; | in->link_hook = hook; | ||||
in->link_func = hook->hook_func; | in->link_mbuf_chk = hook->hook_mbuf_chk; | ||||
in->link_mem_chk = hook->hook_mem_chk; | |||||
in->link_flags = hook->hook_flags; | in->link_flags = hook->hook_flags; | ||||
in->link_ruleset = hook->hook_ruleset; | in->link_ruleset = hook->hook_ruleset; | ||||
if (pa->pa_flags & PFIL_APPEND) | if (pa->pa_flags & PFIL_APPEND) | ||||
CK_STAILQ_INSERT_TAIL(&head->head_in, in, link_chain); | CK_STAILQ_INSERT_TAIL(&head->head_in, in, link_chain); | ||||
else | else | ||||
CK_STAILQ_INSERT_HEAD(&head->head_in, in, link_chain); | CK_STAILQ_INSERT_HEAD(&head->head_in, in, link_chain); | ||||
hook->hook_links++; | hook->hook_links++; | ||||
head->head_nhooksin++; | head->head_nhooksin++; | ||||
} | } | ||||
if (pa->pa_flags & PFIL_OUT) { | if (pa->pa_flags & PFIL_OUT) { | ||||
out->link_hook = hook; | out->link_hook = hook; | ||||
out->link_func = hook->hook_func; | out->link_mbuf_chk = hook->hook_mbuf_chk; | ||||
out->link_mem_chk = hook->hook_mem_chk; | |||||
out->link_flags = hook->hook_flags; | out->link_flags = hook->hook_flags; | ||||
out->link_ruleset = hook->hook_ruleset; | out->link_ruleset = hook->hook_ruleset; | ||||
if (pa->pa_flags & PFIL_APPEND) | if (pa->pa_flags & PFIL_APPEND) | ||||
CK_STAILQ_INSERT_HEAD(&head->head_out, out, link_chain); | CK_STAILQ_INSERT_HEAD(&head->head_out, out, link_chain); | ||||
else | else | ||||
CK_STAILQ_INSERT_TAIL(&head->head_out, out, link_chain); | CK_STAILQ_INSERT_TAIL(&head->head_out, out, link_chain); | ||||
hook->hook_links++; | hook->hook_links++; | ||||
head->head_nhooksout++; | head->head_nhooksout++; | ||||
▲ Show 20 Lines • Show All 272 Lines • Show Last 20 Lines |
stray newline?