Changeset View
Changeset View
Standalone View
Standalone View
sys/netpfil/ipfw/ip_fw_pfil.c
Show First 20 Lines • Show All 120 Lines • ▼ Show 20 Lines | |||||
* The packet may be consumed. | * The packet may be consumed. | ||||
*/ | */ | ||||
int | int | ||||
ipfw_check_packet(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir, | ipfw_check_packet(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir, | ||||
struct inpcb *inp) | struct inpcb *inp) | ||||
{ | { | ||||
struct ip_fw_args args; | struct ip_fw_args args; | ||||
struct m_tag *tag; | struct m_tag *tag; | ||||
int ipfw; | void *psa; | ||||
int ret; | int ipfw, ret; | ||||
/* convert dir to IPFW values */ | /* convert dir to IPFW values */ | ||||
dir = (dir == PFIL_IN) ? DIR_IN : DIR_OUT; | dir = (dir == PFIL_IN) ? DIR_IN : DIR_OUT; | ||||
bzero(&args, sizeof(args)); | args.flags = 0; | ||||
again: | again: | ||||
/* | /* | ||||
* extract and remove the tag if present. If we are left | * extract and remove the tag if present. If we are left | ||||
* with onepass, optimize the outgoing path. | * with onepass, optimize the outgoing path. | ||||
*/ | */ | ||||
tag = m_tag_locate(*m0, MTAG_IPFW_RULE, 0, NULL); | tag = m_tag_locate(*m0, MTAG_IPFW_RULE, 0, NULL); | ||||
if (tag != NULL) { | if (tag != NULL) { | ||||
args.rule = *((struct ipfw_rule_ref *)(tag+1)); | args.rule = *((struct ipfw_rule_ref *)(tag+1)); | ||||
m_tag_delete(*m0, tag); | m_tag_delete(*m0, tag); | ||||
if (args.rule.info & IPFW_ONEPASS) | if (args.rule.info & IPFW_ONEPASS) | ||||
return (0); | return (0); | ||||
args.flags |= IPFW_ARGS_REF; | |||||
} | } | ||||
args.m = *m0; | args.m = *m0; | ||||
args.oif = dir == DIR_OUT ? ifp : NULL; | args.oif = dir == DIR_OUT ? ifp : NULL; | ||||
args.inp = inp; | args.inp = inp; | ||||
ipfw = ipfw_chk(&args); | ipfw = ipfw_chk(&args); | ||||
*m0 = args.m; | *m0 = args.m; | ||||
KASSERT(*m0 != NULL || ipfw == IP_FW_DENY, ("%s: m0 is NULL", | KASSERT(*m0 != NULL || ipfw == IP_FW_DENY, ("%s: m0 is NULL", | ||||
__func__)); | __func__)); | ||||
/* breaking out of the switch means drop */ | /* breaking out of the switch means drop */ | ||||
ret = 0; /* default return value for pass */ | |||||
switch (ipfw) { | switch (ipfw) { | ||||
case IP_FW_PASS: | case IP_FW_PASS: | ||||
/* next_hop may be set by ipfw_chk */ | /* next_hop may be set by ipfw_chk */ | ||||
if (args.next_hop == NULL && args.next_hop6 == NULL) | if ((args.flags & (IPFW_ARGS_NH4 | IPFW_ARGS_NH4PTR | | ||||
break; /* pass */ | IPFW_ARGS_NH6 | IPFW_ARGS_NH6PTR)) == 0) { | ||||
ret = 0; | |||||
break; | |||||
} | |||||
#ifdef INET | |||||
if (args.flags & (IPFW_ARGS_NH4 | IPFW_ARGS_NH4PTR)) { | |||||
MPASS((args.flags & (IPFW_ARGS_NH4 | | |||||
IPFW_ARGS_NH4PTR)) != (IPFW_ARGS_NH4 | | |||||
IPFW_ARGS_NH4PTR)); | |||||
MPASS((args.flags & (IPFW_ARGS_NH6 | | |||||
IPFW_ARGS_NH6PTR)) == 0); | |||||
ret = sizeof(struct sockaddr_in); | |||||
melifaro: Would it be possible to retain //len// variable here? Mixing error code and length in one… | |||||
psa = (args.flags & IPFW_ARGS_NH4) ? | |||||
&args.hopstore : args.next_hop; | |||||
if (in_localip(satosin(psa)->sin_addr)) | |||||
(*m0)->m_flags |= M_FASTFWD_OURS; | |||||
(*m0)->m_flags |= M_IP_NEXTHOP; | |||||
} | |||||
#endif | |||||
#ifdef INET6 | |||||
if (args.flags & (IPFW_ARGS_NH6 | IPFW_ARGS_NH6PTR)) { | |||||
MPASS((args.flags & (IPFW_ARGS_NH6 | | |||||
IPFW_ARGS_NH6PTR)) != (IPFW_ARGS_NH6 | | |||||
IPFW_ARGS_NH6PTR)); | |||||
MPASS((args.flags & (IPFW_ARGS_NH4 | | |||||
IPFW_ARGS_NH4PTR)) == 0); | |||||
ret = sizeof(struct sockaddr_in6); | |||||
psa = args.next_hop6; | |||||
(*m0)->m_flags |= M_IP6_NEXTHOP; | |||||
} | |||||
#endif | |||||
#if (!defined(INET6) && !defined(INET)) | #if (!defined(INET6) && !defined(INET)) | ||||
ret = EACCES; | ret = EACCES; | ||||
#else | #else | ||||
{ | /* | ||||
struct m_tag *fwd_tag; | * Incoming packets should not be tagged so we do not | ||||
size_t len; | |||||
KASSERT(args.next_hop == NULL || args.next_hop6 == NULL, | |||||
("%s: both next_hop=%p and next_hop6=%p not NULL", __func__, | |||||
args.next_hop, args.next_hop6)); | |||||
#ifdef INET6 | |||||
if (args.next_hop6 != NULL) | |||||
len = sizeof(struct sockaddr_in6); | |||||
#endif | |||||
#ifdef INET | |||||
if (args.next_hop != NULL) | |||||
len = sizeof(struct sockaddr_in); | |||||
#endif | |||||
/* Incoming packets should not be tagged so we do not | |||||
* m_tag_find. Outgoing packets may be tagged, so we | * m_tag_find. Outgoing packets may be tagged, so we | ||||
* reuse the tag if present. | * reuse the tag if present. | ||||
*/ | */ | ||||
fwd_tag = (dir == DIR_IN) ? NULL : | tag = (dir == DIR_IN) ? NULL : | ||||
m_tag_find(*m0, PACKET_TAG_IPFORWARD, NULL); | m_tag_find(*m0, PACKET_TAG_IPFORWARD, NULL); | ||||
if (fwd_tag != NULL) { | if (tag != NULL) { | ||||
m_tag_unlink(*m0, fwd_tag); | m_tag_unlink(*m0, tag); | ||||
} else { | } else { | ||||
fwd_tag = m_tag_get(PACKET_TAG_IPFORWARD, len, | tag = m_tag_get(PACKET_TAG_IPFORWARD, ret, | ||||
M_NOWAIT); | M_NOWAIT); | ||||
if (fwd_tag == NULL) { | if (tag == NULL) { | ||||
ret = EACCES; | ret = EACCES; | ||||
break; /* i.e. drop */ | break; /* i.e. drop */ | ||||
} | } | ||||
} | } | ||||
if ((args.flags & IPFW_ARGS_NH6) == 0) | |||||
bcopy(psa, tag + 1, ret); | |||||
m_tag_prepend(*m0, tag); | |||||
ret = 0; | |||||
#ifdef INET6 | #ifdef INET6 | ||||
if (args.next_hop6 != NULL) { | if (args.flags & (IPFW_ARGS_NH6 | IPFW_ARGS_NH6PTR)) { | ||||
struct sockaddr_in6 *sa6; | struct sockaddr_in6 *sa6; | ||||
sa6 = (struct sockaddr_in6 *)(fwd_tag + 1); | sa6 = satosin6(tag + 1); | ||||
bcopy(args.next_hop6, sa6, len); | if (args.flags & IPFW_ARGS_NH6) { | ||||
sa6->sin6_family = AF_INET6; | |||||
sa6->sin6_len = sizeof(*sa6); | |||||
sa6->sin6_addr = args.hopstore6.sin6_addr; | |||||
sa6->sin6_port = args.hopstore6.sin6_port; | |||||
sa6->sin6_scope_id = | |||||
args.hopstore6.sin6_scope_id; | |||||
} | |||||
/* | /* | ||||
* If nh6 address is link-local we should convert | * If nh6 address is link-local we should convert | ||||
* it to kernel internal form before doing any | * it to kernel internal form before doing any | ||||
* comparisons. | * comparisons. | ||||
*/ | */ | ||||
if (sa6_embedscope(sa6, V_ip6_use_defzone) != 0) { | if (sa6_embedscope(sa6, V_ip6_use_defzone) != 0) { | ||||
ret = EACCES; | ret = EACCES; | ||||
break; | break; | ||||
} | } | ||||
if (in6_localip(&sa6->sin6_addr)) | if (in6_localip(&sa6->sin6_addr)) | ||||
(*m0)->m_flags |= M_FASTFWD_OURS; | (*m0)->m_flags |= M_FASTFWD_OURS; | ||||
(*m0)->m_flags |= M_IP6_NEXTHOP; | |||||
} | } | ||||
#endif | #endif /* INET6 */ | ||||
#ifdef INET | |||||
if (args.next_hop != NULL) { | |||||
bcopy(args.next_hop, (fwd_tag+1), len); | |||||
if (in_localip(args.next_hop->sin_addr)) | |||||
(*m0)->m_flags |= M_FASTFWD_OURS; | |||||
(*m0)->m_flags |= M_IP_NEXTHOP; | |||||
} | |||||
#endif | |||||
m_tag_prepend(*m0, fwd_tag); | |||||
} | |||||
#endif /* INET || INET6 */ | #endif /* INET || INET6 */ | ||||
break; | break; | ||||
case IP_FW_DENY: | case IP_FW_DENY: | ||||
ret = EACCES; | ret = EACCES; | ||||
break; /* i.e. drop */ | break; /* i.e. drop */ | ||||
case IP_FW_DUMMYNET: | case IP_FW_DUMMYNET: | ||||
ret = EACCES; | ret = EACCES; | ||||
if (ip_dn_io_ptr == NULL) | if (ip_dn_io_ptr == NULL) | ||||
break; /* i.e. drop */ | break; /* i.e. drop */ | ||||
MPASS(args->flags & IPFW_ARGS_REF); | |||||
if (mtod(*m0, struct ip *)->ip_v == 4) | if (mtod(*m0, struct ip *)->ip_v == 4) | ||||
ret = ip_dn_io_ptr(m0, dir, &args); | ret = ip_dn_io_ptr(m0, dir, &args); | ||||
else if (mtod(*m0, struct ip *)->ip_v == 6) | else if (mtod(*m0, struct ip *)->ip_v == 6) | ||||
ret = ip_dn_io_ptr(m0, dir | PROTO_IPV6, &args); | ret = ip_dn_io_ptr(m0, dir | PROTO_IPV6, &args); | ||||
else | else | ||||
break; /* drop it */ | break; /* drop it */ | ||||
/* | /* | ||||
* XXX should read the return value. | * XXX should read the return value. | ||||
* dummynet normally eats the packet and sets *m0=NULL | * dummynet normally eats the packet and sets *m0=NULL | ||||
* unless the packet can be sent immediately. In this | * unless the packet can be sent immediately. In this | ||||
* case args is updated and we should re-run the | * case args is updated and we should re-run the | ||||
* check without clearing args. | * check without clearing args. | ||||
*/ | */ | ||||
if (*m0 != NULL) | if (*m0 != NULL) | ||||
goto again; | goto again; | ||||
break; | break; | ||||
case IP_FW_TEE: | case IP_FW_TEE: | ||||
case IP_FW_DIVERT: | case IP_FW_DIVERT: | ||||
if (ip_divert_ptr == NULL) { | if (ip_divert_ptr == NULL) { | ||||
ret = EACCES; | ret = EACCES; | ||||
break; /* i.e. drop */ | break; /* i.e. drop */ | ||||
} | } | ||||
MPASS(args->flags & IPFW_ARGS_REF); | |||||
ret = ipfw_divert(m0, dir, &args.rule, | ret = ipfw_divert(m0, dir, &args.rule, | ||||
(ipfw == IP_FW_TEE) ? 1 : 0); | (ipfw == IP_FW_TEE) ? 1 : 0); | ||||
/* continue processing for the original packet (tee). */ | /* continue processing for the original packet (tee). */ | ||||
if (*m0) | if (*m0) | ||||
goto again; | goto again; | ||||
break; | break; | ||||
case IP_FW_NGTEE: | case IP_FW_NGTEE: | ||||
case IP_FW_NETGRAPH: | case IP_FW_NETGRAPH: | ||||
if (ng_ipfw_input_p == NULL) { | if (ng_ipfw_input_p == NULL) { | ||||
ret = EACCES; | ret = EACCES; | ||||
break; /* i.e. drop */ | break; /* i.e. drop */ | ||||
} | } | ||||
MPASS(args->flags & IPFW_ARGS_REF); | |||||
ret = ng_ipfw_input_p(m0, dir, &args, | ret = ng_ipfw_input_p(m0, dir, &args, | ||||
(ipfw == IP_FW_NGTEE) ? 1 : 0); | (ipfw == IP_FW_NGTEE) ? 1 : 0); | ||||
if (ipfw == IP_FW_NGTEE) /* ignore errors for NGTEE */ | if (ipfw == IP_FW_NGTEE) /* ignore errors for NGTEE */ | ||||
goto again; /* continue with packet */ | goto again; /* continue with packet */ | ||||
break; | break; | ||||
case IP_FW_NAT: | case IP_FW_NAT: | ||||
/* honor one-pass in case of successful nat */ | /* honor one-pass in case of successful nat */ | ||||
if (V_fw_one_pass) | if (V_fw_one_pass) { | ||||
break; /* ret is already 0 */ | ret = 0; | ||||
break; | |||||
} | |||||
goto again; | goto again; | ||||
case IP_FW_REASS: | case IP_FW_REASS: | ||||
goto again; /* continue with packet */ | goto again; /* continue with packet */ | ||||
default: | default: | ||||
KASSERT(0, ("%s: unknown retval", __func__)); | KASSERT(0, ("%s: unknown retval", __func__)); | ||||
} | } | ||||
if (ret != 0) { | if (ret != 0) { | ||||
if (*m0) | if (*m0) | ||||
FREE_PKT(*m0); | FREE_PKT(*m0); | ||||
*m0 = NULL; | *m0 = NULL; | ||||
} | } | ||||
return ret; | return (ret); | ||||
} | } | ||||
/* | /* | ||||
* ipfw processing for ethernet packets (in and out). | * ipfw processing for ethernet packets (in and out). | ||||
*/ | */ | ||||
int | int | ||||
ipfw_check_frame(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir, | ipfw_check_frame(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir, | ||||
struct inpcb *inp) | struct inpcb *inp) | ||||
{ | { | ||||
struct ether_header *eh; | struct ip_fw_args args; | ||||
struct ether_header save_eh; | struct ether_header save_eh; | ||||
struct ether_header *eh; | |||||
struct m_tag *mtag; | |||||
struct mbuf *m; | struct mbuf *m; | ||||
int i, ret; | int i, ret; | ||||
struct ip_fw_args args; | |||||
struct m_tag *mtag; | |||||
bzero(&args, sizeof(args)); | args.flags = IPFW_ARGS_ETHER; | ||||
again: | again: | ||||
/* fetch start point from rule, if any. remove the tag if present. */ | /* fetch start point from rule, if any. remove the tag if present. */ | ||||
mtag = m_tag_locate(*m0, MTAG_IPFW_RULE, 0, NULL); | mtag = m_tag_locate(*m0, MTAG_IPFW_RULE, 0, NULL); | ||||
if (mtag != NULL) { | if (mtag != NULL) { | ||||
args.rule = *((struct ipfw_rule_ref *)(mtag+1)); | args.rule = *((struct ipfw_rule_ref *)(mtag+1)); | ||||
m_tag_delete(*m0, mtag); | m_tag_delete(*m0, mtag); | ||||
if (args.rule.info & IPFW_ONEPASS) | if (args.rule.info & IPFW_ONEPASS) | ||||
return (0); | return (0); | ||||
args.flags |= IPFW_ARGS_REF; | |||||
} | } | ||||
/* I need some amt of data to be contiguous */ | /* I need some amt of data to be contiguous */ | ||||
m = *m0; | m = *m0; | ||||
i = min(m->m_pkthdr.len, max_protohdr); | i = min(m->m_pkthdr.len, max_protohdr); | ||||
if (m->m_len < i) { | if (m->m_len < i) { | ||||
m = m_pullup(m, i); | m = m_pullup(m, i); | ||||
if (m == NULL) { | if (m == NULL) { | ||||
*m0 = m; | *m0 = m; | ||||
return (0); | return (0); | ||||
} | } | ||||
} | } | ||||
eh = mtod(m, struct ether_header *); | eh = mtod(m, struct ether_header *); | ||||
save_eh = *eh; /* save copy for restore below */ | save_eh = *eh; /* save copy for restore below */ | ||||
m_adj(m, ETHER_HDR_LEN); /* strip ethernet header */ | m_adj(m, ETHER_HDR_LEN); /* strip ethernet header */ | ||||
args.m = m; /* the packet we are looking at */ | args.m = m; /* the packet we are looking at */ | ||||
args.oif = dir == PFIL_OUT ? ifp: NULL; /* destination, if any */ | args.oif = dir == PFIL_OUT ? ifp: NULL; /* destination, if any */ | ||||
args.next_hop = NULL; /* we do not support forward yet */ | |||||
args.next_hop6 = NULL; /* we do not support forward yet */ | |||||
args.eh = &save_eh; /* MAC header for bridged/MAC packets */ | args.eh = &save_eh; /* MAC header for bridged/MAC packets */ | ||||
args.inp = NULL; /* used by ipfw uid/gid/jail rules */ | args.inp = inp; /* used by ipfw uid/gid/jail rules */ | ||||
i = ipfw_chk(&args); | i = ipfw_chk(&args); | ||||
m = args.m; | m = args.m; | ||||
if (m != NULL) { | if (m != NULL) { | ||||
/* | /* | ||||
* Restore Ethernet header, as needed, in case the | * Restore Ethernet header, as needed, in case the | ||||
* mbuf chain was replaced by ipfw. | * mbuf chain was replaced by ipfw. | ||||
*/ | */ | ||||
M_PREPEND(m, ETHER_HDR_LEN, M_NOWAIT); | M_PREPEND(m, ETHER_HDR_LEN, M_NOWAIT); | ||||
Show All 14 Lines | case IP_FW_PASS: | ||||
break; | break; | ||||
case IP_FW_DENY: | case IP_FW_DENY: | ||||
ret = EACCES; | ret = EACCES; | ||||
break; /* i.e. drop */ | break; /* i.e. drop */ | ||||
case IP_FW_DUMMYNET: | case IP_FW_DUMMYNET: | ||||
ret = EACCES; | ret = EACCES; | ||||
int dir2; | |||||
if (ip_dn_io_ptr == NULL) | if (ip_dn_io_ptr == NULL) | ||||
break; /* i.e. drop */ | break; /* i.e. drop */ | ||||
*m0 = NULL; | *m0 = NULL; | ||||
dir2 = (dir == PFIL_IN) ? DIR_IN : DIR_OUT; | dir = (dir == PFIL_IN) ? DIR_IN : DIR_OUT; | ||||
ip_dn_io_ptr(&m, dir2 | PROTO_LAYER2, &args); | MPASS(args->flags & IPFW_ARGS_REF); | ||||
ip_dn_io_ptr(&m, dir | PROTO_LAYER2, &args); | |||||
return 0; | return 0; | ||||
case IP_FW_NGTEE: | case IP_FW_NGTEE: | ||||
case IP_FW_NETGRAPH: | case IP_FW_NETGRAPH: | ||||
if (ng_ipfw_input_p == NULL) { | if (ng_ipfw_input_p == NULL) { | ||||
ret = EACCES; | ret = EACCES; | ||||
break; /* i.e. drop */ | break; /* i.e. drop */ | ||||
} | } | ||||
MPASS(args->flags & IPFW_ARGS_REF); | |||||
ret = ng_ipfw_input_p(m0, (dir == PFIL_IN) ? DIR_IN : DIR_OUT, | ret = ng_ipfw_input_p(m0, (dir == PFIL_IN) ? DIR_IN : DIR_OUT, | ||||
&args, (i == IP_FW_NGTEE) ? 1 : 0); | &args, (i == IP_FW_NGTEE) ? 1 : 0); | ||||
if (i == IP_FW_NGTEE) /* ignore errors for NGTEE */ | if (i == IP_FW_NGTEE) /* ignore errors for NGTEE */ | ||||
goto again; /* continue with packet */ | goto again; /* continue with packet */ | ||||
break; | break; | ||||
default: | default: | ||||
KASSERT(0, ("%s: unknown retval", __func__)); | KASSERT(0, ("%s: unknown retval", __func__)); | ||||
} | } | ||||
if (ret != 0) { | if (ret != 0) { | ||||
if (*m0) | if (*m0) | ||||
FREE_PKT(*m0); | FREE_PKT(*m0); | ||||
*m0 = NULL; | *m0 = NULL; | ||||
} | } | ||||
return ret; | return (ret); | ||||
} | } | ||||
/* do the divert, return 1 on error 0 on success */ | /* do the divert, return 1 on error 0 on success */ | ||||
static int | static int | ||||
ipfw_divert(struct mbuf **m0, int incoming, struct ipfw_rule_ref *rule, | ipfw_divert(struct mbuf **m0, int incoming, struct ipfw_rule_ref *rule, | ||||
int tee) | int tee) | ||||
{ | { | ||||
/* | /* | ||||
▲ Show 20 Lines • Show All 174 Lines • Show Last 20 Lines |
Would it be possible to retain len variable here? Mixing error code and length in one variable in the same piece of code will eventually lead to one bug or another..