diff options
Diffstat (limited to 'sys/netinet')
-rw-r--r-- | sys/netinet/ip_divert.c | 56 | ||||
-rw-r--r-- | sys/netinet/ip_divert.h | 63 | ||||
-rw-r--r-- | sys/netinet/ip_dummynet.h | 6 | ||||
-rw-r--r-- | sys/netinet/ipfw/ip_dummynet.c | 44 | ||||
-rw-r--r-- | sys/netinet/ipfw/ip_fw2.c | 156 | ||||
-rw-r--r-- | sys/netinet/ipfw/ip_fw_dynamic.c | 6 | ||||
-rw-r--r-- | sys/netinet/ipfw/ip_fw_log.c | 28 | ||||
-rw-r--r-- | sys/netinet/ipfw/ip_fw_nat.c | 6 | ||||
-rw-r--r-- | sys/netinet/ipfw/ip_fw_pfil.c | 111 | ||||
-rw-r--r-- | sys/netinet/ipfw/ip_fw_private.h | 72 | ||||
-rw-r--r-- | sys/netinet/ipfw/ip_fw_sockopt.c | 3 |
11 files changed, 225 insertions, 326 deletions
diff --git a/sys/netinet/ip_divert.c b/sys/netinet/ip_divert.c index 85c619f..811ca06 100644 --- a/sys/netinet/ip_divert.c +++ b/sys/netinet/ip_divert.c @@ -52,19 +52,12 @@ __FBSDID("$FreeBSD$"); #include <sys/priv.h> #include <sys/proc.h> #include <sys/protosw.h> -#include <sys/rwlock.h> -#include <sys/signalvar.h> #include <sys/socket.h> #include <sys/socketvar.h> -#include <sys/sx.h> #include <sys/sysctl.h> -#include <sys/systm.h> - -#include <vm/uma.h> #include <net/if.h> #include <net/netisr.h> -#include <net/route.h> #include <net/vnet.h> #include <netinet/in.h> @@ -72,7 +65,6 @@ __FBSDID("$FreeBSD$"); #include <netinet/in_systm.h> #include <netinet/in_var.h> #include <netinet/ip.h> -#include <netinet/ip_divert.h> #include <netinet/ip_var.h> #include <netinet/ip_fw.h> #include <netinet/ipfw/ip_fw_private.h> @@ -194,7 +186,7 @@ div_destroy(void) * IPPROTO_DIVERT is not in the real IP protocol number space; this * function should never be called. Just in case, drop any packets. */ -void +static void div_input(struct mbuf *m, int off) { @@ -218,9 +210,8 @@ divert_packet(struct mbuf *m, int incoming) struct sockaddr_in divsrc; struct m_tag *mtag; - mtag = m_tag_find(m, PACKET_TAG_DIVERT, NULL); + mtag = m_tag_locate(m, MTAG_IPFW_RULE, 0, NULL); if (mtag == NULL) { - printf("%s: no divert tag\n", __func__); m_freem(m); return; } @@ -245,14 +236,15 @@ divert_packet(struct mbuf *m, int incoming) ip->ip_len = htons(ip->ip_len); } #endif + bzero(&divsrc, sizeof(divsrc)); + divsrc.sin_len = sizeof(divsrc); + divsrc.sin_family = AF_INET; + /* record matching rule, in host format */ + divsrc.sin_port = ((struct ipfw_rule_ref *)(mtag+1))->rulenum; /* * Record receive interface address, if any. * But only for incoming packets. */ - bzero(&divsrc, sizeof(divsrc)); - divsrc.sin_len = sizeof(divsrc); - divsrc.sin_family = AF_INET; - divsrc.sin_port = divert_cookie(mtag); /* record matching rule */ if (incoming) { struct ifaddr *ifa; struct ifnet *ifp; @@ -300,7 +292,7 @@ divert_packet(struct mbuf *m, int incoming) /* Put packet on socket queue, if any */ sa = NULL; - nport = htons((u_int16_t)divert_info(mtag)); + nport = htons((u_int16_t)(((struct ipfw_rule_ref *)(mtag+1))->info)); INP_INFO_RLOCK(&V_divcbinfo); LIST_FOREACH(inp, &V_divcb, inp_list) { /* XXX why does only one socket match? */ @@ -339,7 +331,7 @@ div_output(struct socket *so, struct mbuf *m, struct sockaddr_in *sin, struct mbuf *control) { struct m_tag *mtag; - struct divert_tag *dt; + struct ipfw_rule_ref *dt; int error = 0; struct mbuf *options; @@ -354,23 +346,31 @@ div_output(struct socket *so, struct mbuf *m, struct sockaddr_in *sin, if (control) m_freem(control); /* XXX */ - if ((mtag = m_tag_find(m, PACKET_TAG_DIVERT, NULL)) == NULL) { - mtag = m_tag_get(PACKET_TAG_DIVERT, sizeof(struct divert_tag), - M_NOWAIT | M_ZERO); + mtag = m_tag_locate(m, MTAG_IPFW_RULE, 0, NULL); + if (mtag == NULL) { + /* this should be normal */ + mtag = m_tag_alloc(MTAG_IPFW_RULE, 0, + sizeof(struct ipfw_rule_ref), M_NOWAIT | M_ZERO); if (mtag == NULL) { error = ENOBUFS; goto cantsend; } - dt = (struct divert_tag *)(mtag+1); m_tag_prepend(m, mtag); - } else - dt = (struct divert_tag *)(mtag+1); + } + dt = (struct ipfw_rule_ref *)(mtag+1); /* Loopback avoidance and state recovery */ if (sin) { int i; - dt->cookie = sin->sin_port; + /* set the starting point. We provide a non-zero slot, + * but a non_matching chain_id to skip that info and use + * the rulenum/rule_id. + */ + dt->slot = 1; /* dummy, chain_id is invalid */ + dt->chain_id = 0; + dt->rulenum = sin->sin_port+1; /* host format ? */ + dt->rule_id = 0; /* * Find receive interface with the given name, stuffed * (if it exists) in the sin_zero[] field. @@ -388,7 +388,7 @@ div_output(struct socket *so, struct mbuf *m, struct sockaddr_in *sin, struct ip *const ip = mtod(m, struct ip *); struct inpcb *inp; - dt->info |= IP_FW_DIVERT_OUTPUT_FLAG; + dt->info |= IPFW_IS_DIVERT | IPFW_INFO_OUT; INP_INFO_WLOCK(&V_divcbinfo); inp = sotoinpcb(so); INP_RLOCK(inp); @@ -454,7 +454,7 @@ div_output(struct socket *so, struct mbuf *m, struct sockaddr_in *sin, m_freem(options); } } else { - dt->info |= IP_FW_DIVERT_LOOPBACK_FLAG; + dt->info |= IPFW_IS_DIVERT | IPFW_INFO_IN; if (m->m_pkthdr.rcvif == NULL) { /* * No luck with the name, check by IP address. @@ -588,7 +588,7 @@ div_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam, return div_output(so, m, (struct sockaddr_in *)nam, control); } -void +static void div_ctlinput(int cmd, struct sockaddr *sa, void *vip) { struct in_addr faddr; @@ -801,5 +801,5 @@ static moduledata_t ipdivertmod = { }; DECLARE_MODULE(ipdivert, ipdivertmod, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY); -MODULE_DEPEND(dummynet, ipfw, 2, 2, 2); +MODULE_DEPEND(ipdivert, ipfw, 2, 2, 2); MODULE_VERSION(ipdivert, 1); diff --git a/sys/netinet/ip_divert.h b/sys/netinet/ip_divert.h index 5036355..b8bcf4f 100644 --- a/sys/netinet/ip_divert.h +++ b/sys/netinet/ip_divert.h @@ -36,53 +36,20 @@ #define _NETINET_IP_DIVERT_H_ /* - * Sysctl declaration. - */ -#ifdef SYSCTL_DECL -SYSCTL_DECL(_net_inet_divert); -#endif - -/* - * Divert socket definitions. - */ -struct divert_tag { - u_int32_t info; /* port & flags */ - u_int16_t cookie; /* ipfw rule number */ -}; - -/* - * Return the divert cookie associated with the mbuf; if any. - */ -static __inline u_int16_t -divert_cookie(struct m_tag *mtag) -{ - return ((struct divert_tag *)(mtag+1))->cookie; -} -static __inline u_int16_t -divert_find_cookie(struct mbuf *m) -{ - struct m_tag *mtag = m_tag_find(m, PACKET_TAG_DIVERT, NULL); - return mtag ? divert_cookie(mtag) : 0; -} - -/* - * Return the divert info associated with the mbuf; if any. + * divert has no custom kernel-userland API. + * + * All communication occurs through a sockaddr_in socket where + * + * kernel-->userland + * sin_port = matching rule, host format; + * sin_addr = IN: first address of the incoming interface; + * OUT: INADDR_ANY + * sin_zero = if fits, the interface name (max 7 bytes + NUL) + * + * userland->kernel + * sin_port = restart-rule - 1, host order + * (we restart at sin_port + 1) + * sin_addr = IN: address of the incoming interface; + * OUT: INADDR_ANY */ -static __inline u_int32_t -divert_info(struct m_tag *mtag) -{ - return ((struct divert_tag *)(mtag+1))->info; -} -static __inline u_int32_t -divert_find_info(struct mbuf *m) -{ - struct m_tag *mtag = m_tag_find(m, PACKET_TAG_DIVERT, NULL); - return mtag ? divert_info(mtag) : 0; -} - -typedef void ip_divert_packet_t(struct mbuf *m, int incoming); -extern ip_divert_packet_t *ip_divert_ptr; - -extern void div_input(struct mbuf *, int); -extern void div_ctlinput(int, struct sockaddr *, void *); #endif /* _NETINET_IP_DIVERT_H_ */ diff --git a/sys/netinet/ip_dummynet.h b/sys/netinet/ip_dummynet.h index 4d039ee..3a193e9 100644 --- a/sys/netinet/ip_dummynet.h +++ b/sys/netinet/ip_dummynet.h @@ -114,11 +114,7 @@ struct dn_heap { * other forms of packet reinjection. */ struct dn_pkt_tag { - /* first part, reinject info */ - uint32_t slot; /* slot of next rule to use */ - uint32_t rulenum; /* matching rule number */ - uint32_t rule_id; /* matching rule id */ - uint32_t chain_id; /* ruleset id */ + struct ipfw_rule_ref rule; /* matching rule */ /* second part, dummynet specific */ int dn_dir; /* action when packet comes out. */ diff --git a/sys/netinet/ipfw/ip_dummynet.c b/sys/netinet/ipfw/ip_dummynet.c index 2142751..7126ace 100644 --- a/sys/netinet/ipfw/ip_dummynet.c +++ b/sys/netinet/ipfw/ip_dummynet.c @@ -462,15 +462,10 @@ heap_free(struct dn_heap *h) */ /* - * Dispose a packet in dummynet. Use an inline functions so if we + * Dispose a list of packet. Use an inline functions so if we * need to free extra state associated to a packet, this is a * central point to do it. */ -static __inline void *dn_free_pkt(struct mbuf *m) -{ - m_freem(m); - return NULL; -} static __inline void dn_free_pkts(struct mbuf *mnext) { @@ -478,7 +473,7 @@ static __inline void dn_free_pkts(struct mbuf *mnext) while ((m = mnext) != NULL) { mnext = m->m_nextpkt; - dn_free_pkt(m); + FREE_PKT(m); } } @@ -968,24 +963,31 @@ dummynet_send(struct mbuf *m) for (; m != NULL; m = n) { struct ifnet *ifp; int dst; + struct m_tag *tag; n = m->m_nextpkt; m->m_nextpkt = NULL; - if (m_tag_first(m) == NULL) { + tag = m_tag_first(m); + if (tag == NULL) { dst = DIR_DROP; } else { struct dn_pkt_tag *pkt = dn_tag_get(m); + /* extract the dummynet info, rename the tag */ dst = pkt->dn_dir; ifp = pkt->ifp; + /* rename the tag so it carries reinject info */ + tag->m_tag_cookie = MTAG_IPFW_RULE; + tag->m_tag_id = 0; } switch (dst) { case DIR_OUT: + SET_HOST_IPLEN(mtod(m, struct ip *)); ip_output(m, NULL, NULL, IP_FORWARDING, NULL, NULL); break ; case DIR_IN : /* put header in network format for ip_input() */ - SET_NET_IPLEN(mtod(m, struct ip *)); + //SET_NET_IPLEN(mtod(m, struct ip *)); netisr_dispatch(NETISR_IP, m); break; #ifdef INET6 @@ -994,6 +996,7 @@ dummynet_send(struct mbuf *m) break; case DIR_OUT | PROTO_IPV6: + SET_HOST_IPLEN(mtod(m, struct ip *)); ip6_output(m, NULL, NULL, IPV6_FORWARDING, NULL, NULL, NULL); break; #endif @@ -1024,12 +1027,12 @@ dummynet_send(struct mbuf *m) case DIR_DROP: /* drop the packet after some time */ - dn_free_pkt(m); + FREE_PKT(m); break; default: printf("dummynet: bad switch %d!\n", dst); - dn_free_pkt(m); + FREE_PKT(m); break; } } @@ -1362,7 +1365,7 @@ dummynet_io(struct mbuf **m0, int dir, struct ip_fw_args *fwa) struct dn_pipe *pipe; uint64_t len = m->m_pkthdr.len; struct dn_flow_queue *q = NULL; - int is_pipe = fwa->cookie & 0x8000000 ? 0 : 1; + int is_pipe = fwa->rule.info & IPFW_IS_PIPE; KASSERT(m->m_nextpkt == NULL, ("dummynet_io: mbuf queue passed to dummynet")); @@ -1371,16 +1374,13 @@ dummynet_io(struct mbuf **m0, int dir, struct ip_fw_args *fwa) io_pkt++; /* * This is a dummynet rule, so we expect an O_PIPE or O_QUEUE rule. - * - * XXXGL: probably the pipe->fs and fs->pipe logic here - * below can be simplified. */ if (is_pipe) { - pipe = locate_pipe(fwa->cookie & 0xffff); + pipe = locate_pipe(fwa->rule.info & IPFW_INFO_MASK); if (pipe != NULL) fs = &(pipe->fs); } else - fs = locate_flowset(fwa->cookie & 0xffff); + fs = locate_flowset(fwa->rule.info & IPFW_INFO_MASK); if (fs == NULL) goto dropit; /* This queue/pipe does not exist! */ @@ -1426,12 +1426,9 @@ dummynet_io(struct mbuf **m0, int dir, struct ip_fw_args *fwa) * Ok, i can handle the pkt now... * Build and enqueue packet + parameters. */ - pkt->slot = fwa->slot; - pkt->rulenum = fwa->rulenum; - pkt->rule_id = fwa->rule_id; - pkt->chain_id = fwa->chain_id; + pkt->rule = fwa->rule; + pkt->rule.info &= IPFW_ONEPASS; /* only keep this info */ pkt->dn_dir = dir; - pkt->ifp = fwa->oif; if (q->head == NULL) @@ -1562,7 +1559,8 @@ dropit: if (q) q->drops++; DUMMYNET_UNLOCK(); - *m0 = dn_free_pkt(m); + FREE_PKT(m); + *m0 = NULL; return ((fs && (fs->flags_fs & DN_NOERROR)) ? 0 : ENOBUFS); } diff --git a/sys/netinet/ipfw/ip_fw2.c b/sys/netinet/ipfw/ip_fw2.c index 58a88dc..b5843268 100644 --- a/sys/netinet/ipfw/ip_fw2.c +++ b/sys/netinet/ipfw/ip_fw2.c @@ -74,7 +74,6 @@ __FBSDID("$FreeBSD$"); #include <netinet/ip_icmp.h> #include <netinet/ip_fw.h> #include <netinet/ipfw/ip_fw_private.h> -#include <netinet/ip_divert.h> #include <netinet/ip_carp.h> #include <netinet/pim.h> #include <netinet/tcp_var.h> @@ -560,7 +559,7 @@ send_reject6(struct ip_fw_args *args, int code, u_int hlen, struct ip6_hdr *ip6) ip6_output(m0, NULL, NULL, 0, NULL, NULL, NULL); } - m_freem(m); + FREE_PKT(m); } else if (code != ICMP6_UNREACH_RST) { /* Send an ICMPv6 unreach. */ #if 0 /* @@ -576,7 +575,7 @@ send_reject6(struct ip_fw_args *args, int code, u_int hlen, struct ip6_hdr *ip6) #endif icmp6_error(m, ICMP6_DST_UNREACH, code, 0); } else - m_freem(m); + FREE_PKT(m); args->m = NULL; } @@ -603,9 +602,7 @@ send_reject(struct ip_fw_args *args, int code, int iplen, struct ip *ip) #endif if (code != ICMP_REJECT_RST) { /* Send an ICMP unreach */ /* We need the IP header in host order for icmp_error(). */ - if (args->eh != NULL) { - SET_HOST_IPLEN(ip); - } + SET_HOST_IPLEN(ip); icmp_error(args->m, ICMP_UNREACH, code, 0L, 0); } else if (args->f_id.proto == IPPROTO_TCP) { struct tcphdr *const tcp = @@ -618,9 +615,9 @@ send_reject(struct ip_fw_args *args, int code, int iplen, struct ip *ip) if (m != NULL) ip_output(m, NULL, NULL, 0, NULL, NULL); } - m_freem(args->m); + FREE_PKT(args->m); } else - m_freem(args->m); + FREE_PKT(args->m); args->m = NULL; } @@ -709,16 +706,18 @@ check_uidgid(ipfw_insn_u32 *insn, int proto, struct ifnet *oif, } /* - * Helper function to write the matching rule into args + * Helper function to set args with info on the rule after the matching + * one. slot is precise, whereas we guess rule_id as they are + * assigned sequentially. */ static inline void set_match(struct ip_fw_args *args, int slot, struct ip_fw_chain *chain) { - args->chain_id = chain->id; - args->slot = slot + 1; /* we use 0 as a marker */ - args->rule_id = chain->map[slot]->id; - args->rulenum = chain->map[slot]->rulenum; + args->rule.chain_id = chain->id; + args->rule.slot = slot + 1; /* we use 0 as a marker */ + args->rule.rule_id = 1 + chain->map[slot]->id; + args->rule.rulenum = chain->map[slot]->rulenum; } /* @@ -743,7 +742,7 @@ set_match(struct ip_fw_args *args, int slot, * args->rule Pointer to the last matching rule (in/out) * args->next_hop Socket we are forwarding to (out). * args->f_id Addresses grabbed from the packet (out) - * args->cookie a cookie depending on rule action + * args->rule.info a cookie depending on rule action * * Return value: * @@ -753,6 +752,8 @@ set_match(struct ip_fw_args *args, int slot, * IP_FW_TEE tee packet, port in m_tag * IP_FW_DUMMYNET to dummynet, pipe in args->cookie * IP_FW_NETGRAPH into netgraph, cookie args->cookie + * args->rule contains the matching rule, + * args->rule.info has additional information. * */ int @@ -797,14 +798,6 @@ ipfw_chk(struct ip_fw_args *args) int ucred_lookup = 0; /* - * divinput_flags If non-zero, set to the IP_FW_DIVERT_*_FLAG - * associated with a packet input on a divert socket. This - * will allow to distinguish traffic and its direction when - * it originates from a divert socket. - */ - u_int divinput_flags = 0; - - /* * oif | args->oif If NULL, ipfw_chk has been called on the * inbound path (ether_input, ip_input). * If non-NULL, ipfw_chk has been called on the outbound path @@ -862,7 +855,6 @@ ipfw_chk(struct ip_fw_args *args) int dyn_dir = MATCH_UNKNOWN; ipfw_dyn_rule *q = NULL; struct ip_fw_chain *chain = &V_layer3_chain; - struct m_tag *mtag; /* * We store in ulp a pointer to the upper layer protocol header. @@ -1090,16 +1082,8 @@ do { \ proto = ip->ip_p; src_ip = ip->ip_src; dst_ip = ip->ip_dst; -#ifndef HAVE_NET_IPLEN - if (args->eh == NULL) { /* on l3 these are in host format */ - offset = ip->ip_off & IP_OFFMASK; - iplen = ip->ip_len; - } else -#endif /* !HAVE_NET_IPLEN */ - { /* otherwise they are in net format */ - offset = ntohs(ip->ip_off) & IP_OFFMASK; - iplen = ntohs(ip->ip_len); - } + offset = ntohs(ip->ip_off) & IP_OFFMASK; + iplen = ntohs(ip->ip_len); pktlen = iplen < pktlen ? iplen : pktlen; if (offset == 0) { @@ -1143,44 +1127,20 @@ do { \ IPFW_RUNLOCK(chain); return (IP_FW_PASS); /* accept */ } - /* XXX divert should be handled same as other tags */ - mtag = m_tag_find(m, PACKET_TAG_DIVERT, NULL); - if (args->slot) { + if (args->rule.slot) { /* * Packet has already been tagged as a result of a previous * match on rule args->rule aka args->rule_id (PIPE, QUEUE, - * REASS, NETGRAPH and similar, never a skipto). + * REASS, NETGRAPH, DIVERT/TEE...) * Validate the slot and continue from the next one * if still present, otherwise do a lookup. */ - if (V_fw_one_pass) { - IPFW_RUNLOCK(chain); - return (IP_FW_PASS); - } - f_pos = (args->chain_id == chain->id) ? - args->slot /* already incremented */ : - ipfw_find_rule(chain, args->rulenum, args->rule_id+1); + f_pos = (args->rule.chain_id == chain->id) ? + args->rule.slot : + ipfw_find_rule(chain, args->rule.rulenum, + args->rule.rule_id); } else { - /* - * Find the starting rule. It can be either the first - * one, or the one after divert_rule if asked so. - */ - int skipto = mtag ? divert_cookie(mtag) : 0; - f_pos = 0; - if (args->eh == NULL && skipto != 0) { - if (skipto >= IPFW_DEFAULT_RULE) { - IPFW_RUNLOCK(chain); - return (IP_FW_DENY); /* invalid */ - } - f_pos = ipfw_find_rule(chain, skipto+1, 0); - } - } - /* reset divert rule to avoid confusion later */ - if (mtag) { - divinput_flags = divert_info(mtag) & - (IP_FW_DIVERT_OUTPUT_FLAG | IP_FW_DIVERT_LOOPBACK_FLAG); - m_tag_delete(m, mtag); } /* @@ -1332,10 +1292,15 @@ do { \ break; case O_DIVERTED: - match = (cmd->arg1 & 1 && divinput_flags & - IP_FW_DIVERT_LOOPBACK_FLAG) || - (cmd->arg1 & 2 && divinput_flags & - IP_FW_DIVERT_OUTPUT_FLAG); + { + /* For diverted packets, args->rule.info + * contains the divert port (in host format) + * reason and direction. + */ + uint32_t i = args->rule.info; + match = (i&IPFW_IS_MASK) == IPFW_IS_DIVERT && + cmd->arg1 & ((i & IPFW_INFO_IN) ? 1 : 2); + } break; case O_PROTO: @@ -1755,6 +1720,7 @@ do { \ break; case O_TAG: { + struct m_tag *mtag; uint32_t tag = (cmd->arg1 == IP_FW_TABLEARG) ? tablearg : cmd->arg1; @@ -1771,12 +1737,13 @@ do { \ if (cmd->len & F_NOT) { /* `untag' action */ if (mtag != NULL) m_tag_delete(m, mtag); + match = 0; } else if (mtag == NULL) { if ((mtag = m_tag_alloc(MTAG_IPFW, tag, 0, M_NOWAIT)) != NULL) m_tag_prepend(m, mtag); + match = 1; } - match = (cmd->len & F_NOT) ? 0: 1; break; } @@ -1786,6 +1753,7 @@ do { \ break; case O_TAGGED: { + struct m_tag *mtag; uint32_t tag = (cmd->arg1 == IP_FW_TABLEARG) ? tablearg : cmd->arg1; @@ -1926,10 +1894,12 @@ do { \ case O_PIPE: case O_QUEUE: set_match(args, f_pos, chain); - args->cookie = (cmd->arg1 == IP_FW_TABLEARG) ? + args->rule.info = (cmd->arg1 == IP_FW_TABLEARG) ? tablearg : cmd->arg1; - if (cmd->opcode == O_QUEUE) - args->cookie |= 0x80000000; + if (cmd->opcode == O_PIPE) + args->rule.info |= IPFW_IS_PIPE; + if (V_fw_one_pass) + args->rule.info |= IPFW_ONEPASS; retval = IP_FW_DUMMYNET; l = 0; /* exit inner loop */ done = 1; /* exit outer loop */ @@ -1942,23 +1912,11 @@ do { \ /* otherwise this is terminal */ l = 0; /* exit inner loop */ done = 1; /* exit outer loop */ - mtag = m_tag_get(PACKET_TAG_DIVERT, - sizeof(struct divert_tag), - M_NOWAIT); - if (mtag == NULL) { - retval = IP_FW_DENY; - } else { - struct divert_tag *dt; - dt = (struct divert_tag *)(mtag+1); - dt->cookie = f->rulenum; - if (cmd->arg1 == IP_FW_TABLEARG) - dt->info = tablearg; - else - dt->info = cmd->arg1; - m_tag_prepend(m, mtag); - retval = (cmd->opcode == O_DIVERT) ? + retval = (cmd->opcode == O_DIVERT) ? IP_FW_DIVERT : IP_FW_TEE; - } + set_match(args, f_pos, chain); + args->rule.info = (cmd->arg1 == IP_FW_TABLEARG) ? + tablearg : cmd->arg1; break; case O_COUNT: @@ -2074,7 +2032,7 @@ do { \ case O_NETGRAPH: case O_NGTEE: set_match(args, f_pos, chain); - args->cookie = (cmd->arg1 == IP_FW_TABLEARG) ? + args->rule.info = (cmd->arg1 == IP_FW_TABLEARG) ? tablearg : cmd->arg1; retval = (cmd->opcode == O_NETGRAPH) ? IP_FW_NETGRAPH : IP_FW_NGTEE; @@ -2126,12 +2084,6 @@ do { \ f->pcnt++; f->bcnt += pktlen; l = 0; /* in any case exit inner loop */ - -#ifndef HAVE_NET_IPLEN - if (args->eh == NULL) - ip_off = ip->ip_off; - else -#endif /* !HAVE_NET_IPLEN */ ip_off = ntohs(ip->ip_off); /* if not fragmented, go to next rule */ @@ -2139,19 +2091,14 @@ do { \ break; /* * ip_reass() expects len & off in host - * byte order: fix them in case we come - * from layer2. + * byte order. */ - if (args->eh != NULL) { - SET_HOST_IPLEN(ip); - } + SET_HOST_IPLEN(ip); args->m = m = ip_reass(m); /* - * IP header checksum fixup after - * reassembly and leave header - * in network byte order. + * do IP header checksum fixup. */ if (m == NULL) { /* fragment got swallowed */ retval = IP_FW_DENY; @@ -2160,10 +2107,7 @@ do { \ ip = mtod(m, struct ip *); hlen = ip->ip_hl << 2; - /* revert len. & off to net format if needed */ - if (args->eh != NULL) { - SET_NET_IPLEN(ip); - } + SET_NET_IPLEN(ip); ip->ip_sum = 0; if (hlen == sizeof(struct ip)) ip->ip_sum = in_cksum_hdr(ip); diff --git a/sys/netinet/ipfw/ip_fw_dynamic.c b/sys/netinet/ipfw/ip_fw_dynamic.c index 70a0147..6d1ac60 100644 --- a/sys/netinet/ipfw/ip_fw_dynamic.c +++ b/sys/netinet/ipfw/ip_fw_dynamic.c @@ -917,7 +917,7 @@ ipfw_send_pkt(struct mbuf *replyto, struct ipfw_flow_id *id, u_int32_t seq, #endif default: /* XXX: log me?!? */ - m_freem(m); + FREE_PKT(m); return (NULL); } dir = ((flags & (TH_SYN | TH_RST)) == TH_SYN); @@ -1002,11 +1002,7 @@ ipfw_send_pkt(struct mbuf *replyto, struct ipfw_flow_id *id, u_int32_t seq, h->ip_hl = sizeof(*h) >> 2; h->ip_tos = IPTOS_LOWDELAY; h->ip_off = 0; -#ifdef HAVE_NET_IPLEN /* XXX do we handle layer2 ? */ h->ip_len = htons(len); -#else - h->ip_len = len; -#endif h->ip_ttl = V_ip_defttl; h->ip_sum = 0; break; diff --git a/sys/netinet/ipfw/ip_fw_log.c b/sys/netinet/ipfw/ip_fw_log.c index e2aa4d2..e9f05e7 100644 --- a/sys/netinet/ipfw/ip_fw_log.c +++ b/sys/netinet/ipfw/ip_fw_log.c @@ -165,16 +165,8 @@ ipfw_log(struct ip_fw *f, u_int hlen, struct ip_fw_args *args, * more info in the header */ mh.mh_data = "DDDDDDSSSSSS\x08\x00"; - if (args->f_id.addr_type == 4) { - /* restore wire format */ - SET_NET_IPLEN(ip); - } } BPF_MTAP(log_if, (struct mbuf *)&mh); - if (args->eh == NULL && args->f_id.addr_type == 4) { - /* restore host format */ - SET_HOST_IPLEN(ip); - } #endif /* !WITHOUT_BPF */ return; } @@ -409,23 +401,15 @@ ipfw_log(struct ip_fw *f, u_int hlen, struct ip_fw_args *args, } else #endif { - int ip_off, ip_len; -#ifndef HAVE_NET_IPLEN - if (args->eh == NULL) { - ip_off = ip->ip_off; - ip_len = ip->ip_len; - } else -#endif /* !HAVE_NET_IPLEN */ - { - ip_off = ntohs(ip->ip_off); - ip_len = ntohs(ip->ip_len); - } - if (ip_off & (IP_MF | IP_OFFMASK)) + int ipoff, iplen; + ipoff = ntohs(ip->ip_off); + iplen = ntohs(ip->ip_len); + if (ipoff & (IP_MF | IP_OFFMASK)) snprintf(SNPARGS(fragment, 0), " (frag %d:%d@%d%s)", - ntohs(ip->ip_id), ip_len - (ip->ip_hl << 2), + ntohs(ip->ip_id), iplen - (ip->ip_hl << 2), offset << 3, - (ip_off & IP_MF) ? "+" : ""); + (ipoff & IP_MF) ? "+" : ""); } } if (oif || m->m_pkthdr.rcvif) diff --git a/sys/netinet/ipfw/ip_fw_nat.c b/sys/netinet/ipfw/ip_fw_nat.c index 549d779..f30b754 100644 --- a/sys/netinet/ipfw/ip_fw_nat.c +++ b/sys/netinet/ipfw/ip_fw_nat.c @@ -219,9 +219,6 @@ ipfw_nat(struct ip_fw_args *args, struct cfg_nat *t, struct mbuf *m) return (IP_FW_DENY); } ip = mtod(mcl, struct ip *); - if (args->eh == NULL) { - SET_NET_IPLEN(ip); - } /* * XXX - Libalias checksum offload 'duct tape': @@ -330,9 +327,6 @@ ipfw_nat(struct ip_fw_args *args, struct cfg_nat *t, struct mbuf *m) mcl->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA; } } - if (args->eh == NULL) { - SET_HOST_IPLEN(ip); - } args->m = mcl; return (IP_FW_NAT); } diff --git a/sys/netinet/ipfw/ip_fw_pfil.c b/sys/netinet/ipfw/ip_fw_pfil.c index 21e9f58..b215d92 100644 --- a/sys/netinet/ipfw/ip_fw_pfil.c +++ b/sys/netinet/ipfw/ip_fw_pfil.c @@ -59,7 +59,6 @@ __FBSDID("$FreeBSD$"); #include <netinet/ip_var.h> #include <netinet/ip_fw.h> #include <netinet/ipfw/ip_fw_private.h> -#include <netinet/ip_divert.h> #include <netinet/ip_dummynet.h> #include <netgraph/ng_ipfw.h> @@ -76,13 +75,13 @@ static VNET_DEFINE(int, fw6_enable) = 1; int ipfw_chg_hook(SYSCTL_HANDLER_ARGS); /* Divert hooks. */ -ip_divert_packet_t *ip_divert_ptr = NULL; +void (*ip_divert_ptr)(struct mbuf *m, int incoming); /* ng_ipfw hooks. */ ng_ipfw_input_t *ng_ipfw_input_p = NULL; /* Forward declarations. */ -static void ipfw_divert(struct mbuf **, int, int); +static int ipfw_divert(struct mbuf **, int, struct ipfw_rule_ref *, int); #ifdef SYSCTL_NODE SYSCTL_DECL(_net_inet_ip_fw); @@ -107,52 +106,38 @@ ipfw_check_hook(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir, struct inpcb *inp) { struct ip_fw_args args; - struct ng_ipfw_tag *ng_tag; - struct m_tag *dn_tag; + struct m_tag *tag; int ipfw; int ret; -#ifdef IPFIREWALL_FORWARD - struct m_tag *fwd_tag; -#endif + + /* all the processing now uses ip_len in net format */ + SET_NET_IPLEN(mtod(*m0, struct ip *)); /* convert dir to IPFW values */ dir = (dir == PFIL_IN) ? DIR_IN : DIR_OUT; bzero(&args, sizeof(args)); - ng_tag = (struct ng_ipfw_tag *)m_tag_locate(*m0, NGM_IPFW_COOKIE, 0, - NULL); - if (ng_tag != NULL) { - KASSERT(ng_tag->dir == dir, - ("ng_ipfw tag with wrong direction")); - args.slot = ng_tag->slot; - args.rulenum = ng_tag->rulenum; - args.rule_id = ng_tag->rule_id; - args.chain_id = ng_tag->chain_id; - m_tag_delete(*m0, (struct m_tag *)ng_tag); - } - again: - dn_tag = m_tag_find(*m0, PACKET_TAG_DUMMYNET, NULL); - if (dn_tag != NULL) { - struct dn_pkt_tag *dt; - - dt = (struct dn_pkt_tag *)(dn_tag+1); - args.slot = dt->slot; - args.rulenum = dt->rulenum; - args.rule_id = dt->rule_id; - args.chain_id = dt->chain_id; - m_tag_delete(*m0, dn_tag); + /* + * extract and remove the tag if present. If we are left + * with onepass, optimize the outgoing path. + */ + tag = m_tag_locate(*m0, MTAG_IPFW_RULE, 0, NULL); + if (tag != NULL) { + args.rule = *((struct ipfw_rule_ref *)(tag+1)); + m_tag_delete(*m0, tag); + if (args.rule.info & IPFW_ONEPASS) { + SET_HOST_IPLEN(mtod(*m0, struct ip *)); + return 0; + } } args.m = *m0; args.oif = dir == DIR_OUT ? ifp : NULL; args.inp = inp; - if (V_fw_one_pass == 0 || args.slot == 0) { - ipfw = ipfw_chk(&args); - *m0 = args.m; - } else - ipfw = IP_FW_PASS; + ipfw = ipfw_chk(&args); + *m0 = args.m; KASSERT(*m0 != NULL || ipfw == IP_FW_DENY, ("%s: m0 is NULL", __func__)); @@ -167,6 +152,9 @@ again: #ifndef IPFIREWALL_FORWARD ret = EACCES; #else + { + struct m_tag *fwd_tag; + /* Incoming packets should not be tagged so we do not * m_tag_find. Outgoing packets may be tagged, so we * reuse the tag if present. @@ -188,6 +176,7 @@ again: if (in_localip(args.next_hop->sin_addr)) (*m0)->m_flags |= M_FASTFWD_OURS; + } #endif break; @@ -222,15 +211,11 @@ again: ret = EACCES; break; /* i.e. drop */ } - ipfw_divert(m0, dir, (ipfw == IP_FW_TEE) ? 1 : 0); - if (*m0) { - /* continue processing for this one. We set - * args.slot=0, but the divert tag is processed - * in ipfw_chk to jump to the right place. - */ - args.slot = 0; - goto again; /* continue with packet */ - } + ret = ipfw_divert(m0, dir, &args.rule, + (ipfw == IP_FW_TEE) ? 1 : 0); + /* continue processing for the original packet (tee). */ + if (*m0) + goto again; break; case IP_FW_NGTEE: @@ -255,14 +240,18 @@ again: if (ret != 0) { if (*m0) - m_freem(*m0); + FREE_PKT(*m0); *m0 = NULL; } + if (*m0) + SET_HOST_IPLEN(mtod(*m0, struct ip *)); return ret; } -static void -ipfw_divert(struct mbuf **m0, int incoming, int tee) +/* do the divert, return 1 on error 0 on success */ +static int +ipfw_divert(struct mbuf **m0, int incoming, struct ipfw_rule_ref *rule, + int tee) { /* * ipfw_chk() has already tagged the packet with the divert tag. @@ -271,6 +260,7 @@ ipfw_divert(struct mbuf **m0, int incoming, int tee) */ struct mbuf *clone; struct ip *ip; + struct m_tag *tag; /* Cloning needed for tee? */ if (tee == 0) { @@ -282,7 +272,7 @@ ipfw_divert(struct mbuf **m0, int incoming, int tee) * chain and continue with the tee-ed packet. */ if (clone == NULL) - return; + return 1; } /* @@ -294,13 +284,14 @@ ipfw_divert(struct mbuf **m0, int incoming, int tee) * we can do it before a 'tee'. */ ip = mtod(clone, struct ip *); - if (!tee && ip->ip_off & (IP_MF | IP_OFFMASK)) { + if (!tee && ntohs(ip->ip_off) & (IP_MF | IP_OFFMASK)) { int hlen; struct mbuf *reass; + SET_HOST_IPLEN(ip); /* ip_reass wants host order */ reass = ip_reass(clone); /* Reassemble packet. */ if (reass == NULL) - return; + return 0; /* not an error */ /* if reass = NULL then it was consumed by ip_reass */ /* * IP header checksum fixup after reassembly and leave header @@ -315,13 +306,20 @@ ipfw_divert(struct mbuf **m0, int incoming, int tee) else ip->ip_sum = in_cksum(reass, hlen); clone = reass; - } else { - /* Convert header to network byte order. */ - SET_NET_IPLEN(ip); } + /* attach a tag to the packet with the reinject info */ + tag = m_tag_alloc(MTAG_IPFW_RULE, 0, + sizeof(struct ipfw_rule_ref), M_NOWAIT); + if (tag == NULL) { + FREE_PKT(clone); + return 1; + } + *((struct ipfw_rule_ref *)(tag+1)) = *rule; + m_tag_prepend(clone, tag); /* Do the dirty job... */ ip_divert_ptr(clone, incoming); + return 0; } /* @@ -330,17 +328,14 @@ ipfw_divert(struct mbuf **m0, int incoming, int tee) static int ipfw_hook(int onoff, int pf) { - const int arg = PFIL_IN | PFIL_OUT | PFIL_WAITOK; struct pfil_head *pfh; pfh = pfil_head_get(PFIL_TYPE_AF, pf); if (pfh == NULL) return ENOENT; - if (onoff) - (void)pfil_add_hook(ipfw_check_hook, NULL, arg, pfh); - else - (void)pfil_remove_hook(ipfw_check_hook, NULL, arg, pfh); + (void) (onoff ? pfil_add_hook : pfil_remove_hook) + (ipfw_check_hook, NULL, PFIL_IN | PFIL_OUT | PFIL_WAITOK, pfh); return 0; } diff --git a/sys/netinet/ipfw/ip_fw_private.h b/sys/netinet/ipfw/ip_fw_private.h index aa1dc57..cfc07aa 100644 --- a/sys/netinet/ipfw/ip_fw_private.h +++ b/sys/netinet/ipfw/ip_fw_private.h @@ -36,6 +36,7 @@ #ifdef _KERNEL #define MTAG_IPFW 1148380143 /* IPFW-tagged cookie */ +#define MTAG_IPFW_RULE 1262273568 /* rule reference */ /* Return values from ipfw_chk() */ enum { @@ -50,10 +51,6 @@ enum { IP_FW_REASS, }; -/* flags for divert mtag */ -#define IP_FW_DIVERT_LOOPBACK_FLAG 0x00080000 -#define IP_FW_DIVERT_OUTPUT_FLAG 0x00100000 - /* * Structure for collecting parameters to dummynet for ip6_output forwarding */ @@ -70,6 +67,39 @@ struct _ip6dn_args { }; /* + * Reference to an ipfw rule that can be carried outside critical sections. + * A rule is identified by rulenum:rule_id which is ordered. + * In version chain_id the rule can be found in slot 'slot', so + * we don't need a lookup if chain_id == chain->id. + * + * On exit from the firewall this structure refers to the rule after + * the matching one (slot points to the new rule; rulenum:rule_id-1 + * is the matching rule), and additional info (e.g. info often contains + * the insn argument or tablearg in the low 16 bits, in host format). + * On entry, the structure is valid if slot>0, and refers to the starting + * rules. 'info' contains the reason for reinject, e.g. divert port, + * divert direction, and so on. + */ +struct ipfw_rule_ref { + uint32_t slot; /* slot for matching rule */ + uint32_t rulenum; /* matching rule number */ + uint32_t rule_id; /* matching rule id */ + uint32_t chain_id; /* ruleset id */ + uint32_t info; /* see below */ +}; + +enum { + IPFW_INFO_MASK = 0x0000ffff, + IPFW_INFO_OUT = 0x00000000, /* outgoing, just for convenience */ + IPFW_INFO_IN = 0x80000000, /* incoming, overloads dir */ + IPFW_ONEPASS = 0x40000000, /* One-pass, do not reinject */ + IPFW_IS_MASK = 0x30000000, /* which source ? */ + IPFW_IS_DIVERT = 0x20000000, + IPFW_IS_DUMMYNET =0x10000000, + IPFW_IS_PIPE = 0x08000000, /* pip1=1, queue = 0 */ +}; + +/* * Arguments for calling ipfw_chk() and dummynet_io(). We put them * all into a structure because this way it is easier and more * efficient to pass variables around and extend the interface. @@ -79,19 +109,19 @@ struct ip_fw_args { struct ifnet *oif; /* output interface */ struct sockaddr_in *next_hop; /* forward address */ - /* chain_id validates 'slot', the location of the pointer to - * a matching rule. - * If invalid, we can lookup the rule using rule_id and rulenum + /* + * On return, it points to the matching rule. + * On entry, rule.slot > 0 means the info is valid and + * contains the the starting rule for an ipfw search. + * If chain_id == chain->id && slot >0 then jump to that slot. + * Otherwise, we locate the first rule >= rulenum:rule_id */ - uint32_t slot; /* slot for matching rule */ - uint32_t rulenum; /* matching rule number */ - uint32_t rule_id; /* matching rule id */ - uint32_t chain_id; /* ruleset id */ + struct ipfw_rule_ref rule; /* match/restart info */ struct ether_header *eh; /* for bridged packets */ struct ipfw_flow_id f_id; /* grabbed from IP header */ - uint32_t cookie; /* a cookie depending on rule action */ + //uint32_t cookie; /* a cookie depending on rule action */ struct inpcb *inp; struct _ip6dn_args dummypar; /* dummynet->ip6_output */ @@ -122,6 +152,9 @@ enum { /* PROTO_OLDBDG = 0x14, unused, old bridge */ }; +/* wrapper for freeing a packet, in case we need to do more work */ +#define FREE_PKT(m) m_freem(m) + /* * Function definitions. */ @@ -256,6 +289,9 @@ int ipfw_del_table_entry(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr, int ipfw_count_table(struct ip_fw_chain *ch, uint32_t tbl, uint32_t *cnt); int ipfw_dump_table(struct ip_fw_chain *ch, ipfw_table *tbl); +/* hooks for divert */ +extern void (*ip_divert_ptr)(struct mbuf *m, int incoming); + /* In ip_fw_nat.c */ extern struct cfg_nat *(*lookup_nat_ptr)(struct nat_list *, int); @@ -277,18 +313,6 @@ typedef int ng_ipfw_input_t(struct mbuf **, int, struct ip_fw_args *, int); extern ng_ipfw_input_t *ng_ipfw_input_p; #define NG_IPFW_LOADED (ng_ipfw_input_p != NULL) -struct ng_ipfw_tag { - struct m_tag mt; /* tag header */ - /* reinject info */ - uint32_t slot; /* slot for next rule */ - uint32_t rulenum; /* matching rule number */ - uint32_t rule_id; /* matching rule id */ - uint32_t chain_id; /* ruleset id */ - int dir; - -// struct ifnet *ifp; /* interface, for ip_output */ -}; - #define TAGSIZ (sizeof(struct ng_ipfw_tag) - sizeof(struct m_tag)) diff --git a/sys/netinet/ipfw/ip_fw_sockopt.c b/sys/netinet/ipfw/ip_fw_sockopt.c index 40d6ed7..10dd861 100644 --- a/sys/netinet/ipfw/ip_fw_sockopt.c +++ b/sys/netinet/ipfw/ip_fw_sockopt.c @@ -65,7 +65,6 @@ __FBSDID("$FreeBSD$"); #include <netinet/in.h> #include <netinet/ip_fw.h> #include <netinet/ipfw/ip_fw_private.h> -#include <netinet/ip_divert.h> #ifdef MAC #include <security/mac/mac_framework.h> @@ -304,6 +303,8 @@ del_entry(struct ip_fw_chain *chain, u_int32_t arg) n++; } } + if (n == 0 && arg == 0) + break; /* special case, flush on empty ruleset */ /* allocate the map, if needed */ if (n > 0) map = get_map(chain, -n, 1 /* locked */); |