Date: Sun, 25 Jan 2009 19:25:41 +0000 (UTC) From: Andrew Thompson <thompsa@FreeBSD.org> To: src-committers@freebsd.org, svn-src-projects@freebsd.org Subject: svn commit: r187696 - in projects/l2filter/sys: net netinet Message-ID: <200901251925.n0PJPfEu025860@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: thompsa Date: Sun Jan 25 19:25:41 2009 New Revision: 187696 URL: http://svn.freebsd.org/changeset/base/187696 Log: Fix up merge conflicts (vimage, arpv2). Submitted by: Gleb Kurtsou Modified: projects/l2filter/sys/net/if_bridge.c projects/l2filter/sys/net/if_ethersubr.c projects/l2filter/sys/netinet/ip_fw2.c projects/l2filter/sys/netinet/ip_fw_pfil.c Modified: projects/l2filter/sys/net/if_bridge.c ============================================================================== --- projects/l2filter/sys/net/if_bridge.c Sun Jan 25 19:18:20 2009 (r187695) +++ projects/l2filter/sys/net/if_bridge.c Sun Jan 25 19:25:41 2009 (r187696) @@ -3013,6 +3013,7 @@ bridge_pfil(struct mbuf **mp, struct ifn } error = 0; + /* Add tag if member or bridge interface has IFF_L2TAG set */ if (((bifp ? bifp->if_flags : 0) | (ifp ? ifp->if_flags : 0)) & IFF_L2TAG) { mtag_ether_header = m_tag_locate(*mp, MTAG_ETHER, MTAG_ETHER_HEADER, Modified: projects/l2filter/sys/net/if_ethersubr.c ============================================================================== --- projects/l2filter/sys/net/if_ethersubr.c Sun Jan 25 19:18:20 2009 (r187695) +++ projects/l2filter/sys/net/if_ethersubr.c Sun Jan 25 19:25:41 2009 (r187696) @@ -65,6 +65,7 @@ #include <net/if_bridgevar.h> #include <net/if_vlan_var.h> #include <net/if_llatbl.h> +#include <net/pfil.h> #include <net/pf_mtag.h> #include <net/vnet.h> @@ -72,8 +73,6 @@ #include <netinet/in.h> #include <netinet/in_var.h> #include <netinet/if_ether.h> -#include <netinet/ip_fw.h> -#include <netinet/ip_dummynet.h> #include <netinet/vinet.h> #endif #ifdef INET6 @@ -144,15 +143,7 @@ MALLOC_DEFINE(M_ARPCOM, "arpcom", "802.* #define senderr(e) do { error = (e); goto bad;} while (0) -#if defined(INET) || defined(INET6) -int -ether_ipfw_chk(struct mbuf **m0, struct ifnet *dst, - struct ip_fw **rule, int shared); -#ifdef VIMAGE_GLOBALS -static int ether_ipfw; -#endif -#endif - +struct pfil_head ether_pfil_hook; /* Packet filter hooks */ /* * Ethernet output routine. @@ -412,20 +403,12 @@ bad: if (m != NULL) int ether_output_frame(struct ifnet *ifp, struct mbuf *m) { -#if defined(INET) || defined(INET6) - INIT_VNET_NET(ifp->if_vnet); - struct ip_fw *rule = ip_dn_claim_rule(m); + int error = 0; - if (IPFW_LOADED && V_ether_ipfw != 0) { - if (ether_ipfw_chk(&m, ifp, &rule, 0) == 0) { - if (m) { - m_freem(m); - return EACCES; /* pkt dropped */ - } else - return 0; /* consumed e.g. in a pipe */ - } - } -#endif + if (PFIL_HOOKED(ðer_pfil_hook) && (ifp->if_flags & IFF_L2FILTER)) + error = pfil_run_hooks(ðer_pfil_hook, &m, ifp, PFIL_OUT, NULL); + if (m == NULL) + return 0; /* consumed e.g. in a pipe */ /* * Queue message on interface, update output statistics if @@ -434,103 +417,6 @@ ether_output_frame(struct ifnet *ifp, st return ((ifp->if_transmit)(ifp, m)); } -#if defined(INET) || defined(INET6) -/* - * ipfw processing for ethernet packets (in and out). - * The second parameter is NULL from ether_demux, and ifp from - * ether_output_frame. - */ -int -ether_ipfw_chk(struct mbuf **m0, struct ifnet *dst, - struct ip_fw **rule, int shared) -{ - INIT_VNET_INET(dst->if_vnet); - struct ether_header *eh; - struct ether_header save_eh; - struct mbuf *m; - int i; - struct ip_fw_args args; - - if (*rule != NULL && V_fw_one_pass) - return 1; /* dummynet packet, already partially processed */ - - /* - * I need some amt of data to be contiguous, and in case others need - * the packet (shared==1) also better be in the first mbuf. - */ - m = *m0; - i = min( m->m_pkthdr.len, max_protohdr); - if ( shared || m->m_len < i) { - m = m_pullup(m, i); - if (m == NULL) { - *m0 = m; - return 0; - } - } - eh = mtod(m, struct ether_header *); - save_eh = *eh; /* save copy for restore below */ - m_adj(m, ETHER_HDR_LEN); /* strip ethernet header */ - - args.m = m; /* the packet we are looking at */ - args.oif = dst; /* destination, if any */ - args.rule = *rule; /* matching rule to restart */ - args.next_hop = NULL; /* we do not support forward yet */ - args.eh = &save_eh; /* MAC header for bridged/MAC packets */ - args.inp = NULL; /* used by ipfw uid/gid/jail rules */ - i = ip_fw_chk_ptr(&args); - m = args.m; - if (m != NULL) { - /* - * Restore Ethernet header, as needed, in case the - * mbuf chain was replaced by ipfw. - */ - M_PREPEND(m, ETHER_HDR_LEN, M_DONTWAIT); - if (m == NULL) { - *m0 = m; - return 0; - } - if (eh != mtod(m, struct ether_header *)) - bcopy(&save_eh, mtod(m, struct ether_header *), - ETHER_HDR_LEN); - } - *m0 = m; - *rule = args.rule; - - if (i == IP_FW_DENY) /* drop */ - return 0; - - KASSERT(m != NULL, ("ether_ipfw_chk: m is NULL")); - - if (i == IP_FW_PASS) /* a PASS rule. */ - return 1; - - if (DUMMYNET_LOADED && (i == IP_FW_DUMMYNET)) { - /* - * Pass the pkt to dummynet, which consumes it. - * If shared, make a copy and keep the original. - */ - if (shared) { - m = m_copypacket(m, M_DONTWAIT); - if (m == NULL) - return 0; - } else { - /* - * Pass the original to dummynet and - * nothing back to the caller - */ - *m0 = NULL ; - } - ip_dn_io_ptr(&m, dst ? DN_TO_ETH_OUT: DN_TO_ETH_DEMUX, &args); - return 0; - } - /* - * XXX at some point add support for divert/forward actions. - * If none of the above matches, we have to drop the pkt. - */ - return 0; -} -#endif - /* * Process a received Ethernet packet; the packet is in the * mbuf chain m with the ethernet header at the front. @@ -728,6 +614,7 @@ void ether_demux(struct ifnet *ifp, struct mbuf *m) { struct ether_header *eh; + struct m_tag *mtag_ether_header; int isr; u_short ether_type; #if defined(NETATALK) @@ -736,22 +623,16 @@ ether_demux(struct ifnet *ifp, struct mb KASSERT(ifp != NULL, ("%s: NULL interface pointer", __func__)); -#if defined(INET) || defined(INET6) - INIT_VNET_NET(ifp->if_vnet); /* - * Allow dummynet and/or ipfw to claim the frame. + * Allow pfil to claim the frame. * Do not do this for PROMISC frames in case we are re-entered. */ - if (IPFW_LOADED && V_ether_ipfw != 0 && !(m->m_flags & M_PROMISC)) { - struct ip_fw *rule = ip_dn_claim_rule(m); - - if (ether_ipfw_chk(&m, NULL, &rule, 0) == 0) { - if (m) - m_freem(m); /* dropped; free mbuf chain */ - return; /* consumed */ - } + if (PFIL_HOOKED(ðer_pfil_hook) && (ifp->if_flags & IFF_L2FILTER) && + !(m->m_flags & M_PROMISC)) { + if (pfil_run_hooks(ðer_pfil_hook, &m, ifp, PFIL_IN, NULL) != 0 || + m == NULL) + return; } -#endif eh = mtod(m, struct ether_header *); ether_type = ntohs(eh->ether_type); @@ -783,6 +664,14 @@ ether_demux(struct ifnet *ifp, struct mb return; } + if (ifp->if_flags & IFF_L2TAG) { + mtag_ether_header = m_tag_alloc(MTAG_ETHER, MTAG_ETHER_HEADER, ETHER_HDR_LEN, M_NOWAIT); + if (mtag_ether_header != NULL) { + memcpy(mtag_ether_header + 1, eh, ETHER_HDR_LEN); + m_tag_prepend(m, mtag_ether_header); + } + } + /* * Reset layer specific mbuf flags to avoid confusing upper layers. * Strip off Ethernet header. @@ -958,10 +847,6 @@ ether_ifdetach(struct ifnet *ifp) SYSCTL_DECL(_net_link); SYSCTL_NODE(_net_link, IFT_ETHER, ether, CTLFLAG_RW, 0, "Ethernet"); -#if defined(INET) || defined(INET6) -SYSCTL_V_INT(V_NET, vnet_net, _net_link_ether, OID_AUTO, ipfw, CTLFLAG_RW, - ether_ipfw, 0, "Pass ether pkts through firewall"); -#endif #if 0 /* @@ -1217,10 +1102,16 @@ ether_free(void *com, u_char type) static int ether_modevent(module_t mod, int type, void *data) { + int err; switch (type) { case MOD_LOAD: if_register_com_alloc(IFT_ETHER, ether_alloc, ether_free); + ether_pfil_hook.ph_type = PFIL_TYPE_IFT; + ether_pfil_hook.ph_af = IFT_ETHER; + if ((err = pfil_head_register(ðer_pfil_hook)) != 0) + printf("%s: WARNING: unable to register pfil hook, " + "error %d\n", __func__, err); break; case MOD_UNLOAD: if_deregister_com_alloc(IFT_ETHER); Modified: projects/l2filter/sys/netinet/ip_fw2.c ============================================================================== --- projects/l2filter/sys/netinet/ip_fw2.c Sun Jan 25 19:18:20 2009 (r187695) +++ projects/l2filter/sys/netinet/ip_fw2.c Sun Jan 25 19:25:41 2009 (r187696) @@ -58,6 +58,7 @@ __FBSDID("$FreeBSD$"); #include <sys/module.h> #include <sys/priv.h> #include <sys/proc.h> +#include <sys/refcount.h> #include <sys/rwlock.h> #include <sys/socket.h> #include <sys/socketvar.h> @@ -66,6 +67,7 @@ __FBSDID("$FreeBSD$"); #include <sys/ucred.h> #include <sys/vimage.h> #include <net/if.h> +#include <net/if_arp.h> #include <net/radix.h> #include <net/route.h> #include <net/pf_mtag.h> @@ -162,9 +164,47 @@ ipfw_nat_cfg_t *ipfw_nat_del_ptr; ipfw_nat_cfg_t *ipfw_nat_get_cfg_ptr; ipfw_nat_cfg_t *ipfw_nat_get_log_ptr; +static __inline int ether_addr_allow(ipfw_ether_addr *want, + ipfw_ether_addr *a) +{ + static ipfw_ether_addr mask = { + .octet = { 0xff, 0xff, 0xff, 0xff, 0xff,0xff }, + .flags = 0 + }; + if ((want->flags & IPFW_EA_CHECK) == 0) + return (1); + + if ((a->flags & IPFW_EA_CHECK) == 0) + return (0); + + if (want->flags & IPFW_EA_MULTICAST) { + return (ETHER_IS_MULTICAST(a->octet)); + } + +#define EA_CMP(a) (*((u_int64_t*)(a)) & *((u_int64_t*)&mask)) + return (EA_CMP(want) == EA_CMP(a)); +#undef EA_CMP +} + +static __inline int ether_addr_allow_dyn(ipfw_ether_addr *want, ipfw_ether_addr *a) +{ + if ((a->flags & IPFW_EA_CHECK) == 0) { + return (1); + } + return (ether_addr_allow(want, a)); +} + +struct table_entry_addr { + u_char len; + u_char __reserved; + struct ether_addr ether_addr; + in_addr_t in_addr; +}; + struct table_entry { - struct radix_node rn[2]; - struct sockaddr_in addr, mask; + struct radix_node in_rn[2], ether_rn[2]; + struct table_entry_addr addr, mask; + int refcnt; u_int32_t value; }; @@ -744,18 +784,6 @@ send_reject6(struct ip_fw_args *args, in */ tcp_respond(NULL, ip6, tcp, m, ack, seq, flags); } else if (code != ICMP6_UNREACH_RST) { /* Send an ICMPv6 unreach. */ -#if 0 - /* - * Unlike above, the mbufs need to line up with the ip6 hdr, - * as the contents are read. We need to m_adj() the - * needed amount. - * The mbuf will however be thrown away so we can adjust it. - * Remember we did an m_pullup on it already so we - * can make some assumptions about contiguousness. - */ - if (args->L3offset) - m_adj(m, args->L3offset); -#endif icmp6_error(m, ICMP6_DST_UNREACH, code, 0); } else m_freem(m); @@ -782,7 +810,6 @@ ipfw_log(struct ip_fw *f, u_int hlen, st struct ip *ip) { INIT_VNET_IPFW(curvnet); - struct ether_header *eh = args->eh; char *action; int limit_reached = 0; char action2[40], proto[128], fragment[32]; @@ -909,7 +936,7 @@ ipfw_log(struct ip_fw *f, u_int hlen, st } if (hlen == 0) { /* non-ip */ - snprintf(SNPARGS(proto, 0), "MAC"); + snprintf(SNPARGS(proto, 0), "ether"); } else { int len; @@ -1011,13 +1038,8 @@ ipfw_log(struct ip_fw *f, u_int hlen, st #endif { int ip_off, ip_len; - if (eh != NULL) { /* layer 2 packets are as on the wire */ - ip_off = ntohs(ip->ip_off); - ip_len = ntohs(ip->ip_len); - } else { - ip_off = ip->ip_off; - ip_len = ip->ip_len; - } + ip_off = ip->ip_off; + ip_len = ip->ip_len; if (ip_off & (IP_MF | IP_OFFMASK)) snprintf(SNPARGS(fragment, 0), " (frag %d:%d@%d%s)", @@ -1244,7 +1266,21 @@ next: if (q == NULL) goto done; /* q = NULL, not found */ - if ( prev != NULL) { /* found and not in front */ + /* + * Only check {src,dst}_ether if it was specified in rule and packet + * mbuf has mtag_ether_header. + */ + if (dir == MATCH_NONE || + !ether_addr_allow_dyn(&q->id.src_ether, + (dir == MATCH_FORWARD ? &pkt->src_ether : &pkt->dst_ether)) || + !ether_addr_allow_dyn(&q->id.dst_ether, + (dir == MATCH_FORWARD ? &pkt->dst_ether : &pkt->src_ether))) { + q = NULL; + dir = MATCH_NONE; + goto done; + } + + if (prev != NULL) { /* found and not in front */ prev->next = q->next; q->next = V_ipfw_dyn_v[i]; V_ipfw_dyn_v[i] = q; @@ -1370,7 +1406,7 @@ realloc_dynamic_table(void) * - "parent" rules for the above (O_LIMIT_PARENT). */ static ipfw_dyn_rule * -add_dyn_rule(struct ipfw_flow_id *id, u_int8_t dyn_type, struct ip_fw *rule) +add_dyn_rule(struct ipfw_flow_id *id, u_int8_t dyn_type, struct ip_fw *rule, uint32_t stateopts) { INIT_VNET_IPFW(curvnet); ipfw_dyn_rule *r; @@ -1403,6 +1439,10 @@ add_dyn_rule(struct ipfw_flow_id *id, u_ } r->id = *id; + if ((stateopts & IP_FW_STATEOPT_ETHER) == 0) { + r->id.src_ether.flags = 0; + r->id.dst_ether.flags = 0; + } r->expire = time_uptime + V_dyn_syn_lifetime; r->rule = rule; r->dyn_type = dyn_type; @@ -1426,7 +1466,7 @@ add_dyn_rule(struct ipfw_flow_id *id, u_ * If the lookup fails, then install one. */ static ipfw_dyn_rule * -lookup_dyn_parent(struct ipfw_flow_id *pkt, struct ip_fw *rule) +lookup_dyn_parent(struct ipfw_flow_id *pkt, struct ip_fw *rule, uint32_t stateopts) { INIT_VNET_IPFW(curvnet); ipfw_dyn_rule *q; @@ -1459,7 +1499,7 @@ lookup_dyn_parent(struct ipfw_flow_id *p return q; } } - return add_dyn_rule(pkt, O_LIMIT_PARENT, rule); + return add_dyn_rule(pkt, O_LIMIT_PARENT, rule, stateopts); } /** @@ -1469,7 +1509,7 @@ lookup_dyn_parent(struct ipfw_flow_id *p * session limitations are enforced. */ static int -install_state(struct ip_fw *rule, ipfw_insn_limit *cmd, +install_state(struct ip_fw *rule, uint32_t stateopts, ipfw_insn_limit *cmd, struct ip_fw_args *args, uint32_t tablearg) { INIT_VNET_IPFW(curvnet); @@ -1517,7 +1557,7 @@ install_state(struct ip_fw *rule, ipfw_i switch (cmd->o.opcode) { case O_KEEP_STATE: /* bidir rule */ - add_dyn_rule(&args->f_id, O_KEEP_STATE, rule); + add_dyn_rule(&args->f_id, O_KEEP_STATE, rule, stateopts); break; case O_LIMIT: { /* limit number of sessions */ @@ -1558,7 +1598,7 @@ install_state(struct ip_fw *rule, ipfw_i id.src_port = args->f_id.src_port; if (limit_mask & DYN_DST_PORT) id.dst_port = args->f_id.dst_port; - if ((parent = lookup_dyn_parent(&id, rule)) == NULL) { + if ((parent = lookup_dyn_parent(&id, rule, stateopts)) == NULL) { printf("ipfw: %s: add parent failed\n", __func__); IPFW_DYN_UNLOCK(); return (1); @@ -1605,7 +1645,7 @@ install_state(struct ip_fw *rule, ipfw_i return (1); } } - add_dyn_rule(&args->f_id, O_LIMIT, (struct ip_fw *)parent); + add_dyn_rule(&args->f_id, O_LIMIT, (struct ip_fw *)parent, stateopts); break; } default: @@ -1725,22 +1765,7 @@ static void send_reject(struct ip_fw_args *args, int code, int ip_len, struct ip *ip) { -#if 0 - /* XXX When ip is not guaranteed to be at mtod() we will - * need to account for this */ - * The mbuf will however be thrown away so we can adjust it. - * Remember we did an m_pullup on it already so we - * can make some assumptions about contiguousness. - */ - if (args->L3offset) - m_adj(m, args->L3offset); -#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) { - ip->ip_len = ntohs(ip->ip_len); - ip->ip_off = ntohs(ip->ip_off); - } icmp_error(args->m, ICMP_UNREACH, code, 0L, 0); } else if (args->f_id.proto == IPPROTO_TCP) { struct tcphdr *const tcp = @@ -1807,86 +1832,149 @@ lookup_next_rule(struct ip_fw *me, u_int return rule; } +static void +init_table_entry_addr(struct table_entry_addr *addr, struct table_entry_addr *mask, + in_addr_t in_addr, uint8_t mlen, ipfw_ether_addr *ether_addr) +{ + addr->len = mask->len = sizeof(struct table_entry_addr); + mask->in_addr = htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0); + addr->in_addr = in_addr & mask->in_addr; + if (ether_addr && (ether_addr->flags & IPFW_EA_CHECK)) { + if (ether_addr->flags & IPFW_EA_MULTICAST) { + bzero(addr->ether_addr.octet, ETHER_ADDR_LEN); + addr->ether_addr.octet[0] = 0x01; + bzero(mask->ether_addr.octet, ETHER_ADDR_LEN); + mask->ether_addr.octet[0] = 0x01; + } else { + memcpy(addr->ether_addr.octet, ether_addr->octet, ETHER_ADDR_LEN); + memset(mask->ether_addr.octet, 0xff, ETHER_ADDR_LEN); + } + } else { + /* set any ether addr */ + bzero(addr->ether_addr.octet, ETHER_ADDR_LEN); + memset(mask->ether_addr.octet, 0xff, ETHER_ADDR_LEN); + } +} + +static __inline struct table_entry * +__rn_to_table_entry(struct radix_node *_rn, int off) +{ + char *rn = (char*) _rn; + + if (rn == NULL) + return NULL; + return (struct table_entry*)(rn - off); + +} + +#define RN_TO_ENT(e, r) (__rn_to_table_entry(e, __offsetof(struct table_entry, r))) + +static __inline void +release_table_entry(struct ipfw_table_head *th, struct table_entry *ent) +{ + IPFW_WLOCK_ASSERT(&V_layer3_chain); /* FIXME */ + + if (refcount_release(&ent->refcnt)) { + if (ent->in_rn[0].rn_flags) + th->in_rnh->rnh_deladdr(&ent->addr, &ent->mask, th->in_rnh); + free(ent, M_IPFW_TBL); + } +} + static int add_table_entry(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr, - uint8_t mlen, uint32_t value) + uint8_t mlen, ipfw_ether_addr *ether_addr, uint32_t value) { INIT_VNET_IPFW(curvnet); - struct radix_node_head *rnh; - struct table_entry *ent; - struct radix_node *rn; + struct ipfw_table_head *th; + struct table_entry *ent, *in_ent; if (tbl >= IPFW_TABLES_MAX) return (EINVAL); - rnh = ch->tables[tbl]; + th = &ch->tables[tbl]; ent = malloc(sizeof(*ent), M_IPFW_TBL, M_NOWAIT | M_ZERO); if (ent == NULL) return (ENOMEM); + refcount_init(&ent->refcnt, 1); ent->value = value; - ent->addr.sin_len = ent->mask.sin_len = 8; - ent->mask.sin_addr.s_addr = htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0); - ent->addr.sin_addr.s_addr = addr & ent->mask.sin_addr.s_addr; + init_table_entry_addr(&ent->addr, &ent->mask, addr, mlen, ether_addr); IPFW_WLOCK(ch); - rn = rnh->rnh_addaddr(&ent->addr, &ent->mask, rnh, (void *)ent); - if (rn == NULL) { + if (th->ether_rnh->rnh_addaddr(&ent->addr, &ent->mask, th->ether_rnh, + ent->ether_rn) == NULL) { IPFW_WUNLOCK(ch); free(ent, M_IPFW_TBL); return (EEXIST); } + in_ent = RN_TO_ENT(th->in_rnh->rnh_lookup(&ent->addr, &ent->mask, th->in_rnh), + in_rn); + if (in_ent == NULL) { + in_ent = RN_TO_ENT(th->in_rnh->rnh_addaddr(&ent->addr, &ent->mask, + th->in_rnh, ent->in_rn), in_rn); + if (in_ent == NULL) { + th->ether_rnh->rnh_deladdr(&ent->addr, &ent->mask, th->ether_rnh); + IPFW_WUNLOCK(ch); + free(ent, M_IPFW_TBL); + return (EEXIST); + } + } + refcount_acquire(&in_ent->refcnt); IPFW_WUNLOCK(ch); return (0); } +static __inline int +delete_table_entry_rn(struct ipfw_table_head *th, void *addr, void *mask) +{ + struct table_entry *ent, *in_ent; + + ent = RN_TO_ENT(th->ether_rnh->rnh_deladdr(addr, mask, th->ether_rnh), + ether_rn); + if (ent == NULL) + return (ESRCH); + in_ent = RN_TO_ENT(th->in_rnh->rnh_lookup(&ent->addr, &ent->mask, th->in_rnh), + in_rn); + release_table_entry(th, in_ent); + release_table_entry(th, ent); + return (0); +} + static int del_table_entry(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr, - uint8_t mlen) + uint8_t mlen, ipfw_ether_addr *ether_addr) { - struct radix_node_head *rnh; - struct table_entry *ent; - struct sockaddr_in sa, mask; + struct ipfw_table_head *th; + struct table_entry_addr sa, mask; + int err; if (tbl >= IPFW_TABLES_MAX) return (EINVAL); - rnh = ch->tables[tbl]; - sa.sin_len = mask.sin_len = 8; - mask.sin_addr.s_addr = htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0); - sa.sin_addr.s_addr = addr & mask.sin_addr.s_addr; + th = &ch->tables[tbl]; + init_table_entry_addr(&sa, &mask, addr, mlen, ether_addr); IPFW_WLOCK(ch); - ent = (struct table_entry *)rnh->rnh_deladdr(&sa, &mask, rnh); - if (ent == NULL) { - IPFW_WUNLOCK(ch); - return (ESRCH); - } + err = delete_table_entry_rn(th, &sa, &mask); IPFW_WUNLOCK(ch); - free(ent, M_IPFW_TBL); - return (0); + return (err); } static int flush_table_entry(struct radix_node *rn, void *arg) { - struct radix_node_head * const rnh = arg; - struct table_entry *ent; - - ent = (struct table_entry *) - rnh->rnh_deladdr(rn->rn_key, rn->rn_mask, rnh); - if (ent != NULL) - free(ent, M_IPFW_TBL); + delete_table_entry_rn((struct ipfw_table_head *)arg, rn->rn_key, rn->rn_mask); return (0); } static int flush_table(struct ip_fw_chain *ch, uint16_t tbl) { - struct radix_node_head *rnh; + struct ipfw_table_head *th; IPFW_WLOCK_ASSERT(ch); if (tbl >= IPFW_TABLES_MAX) return (EINVAL); - rnh = ch->tables[tbl]; - KASSERT(rnh != NULL, ("NULL IPFW table")); - rnh->rnh_walktree(rnh, flush_table_entry, rnh); + th = &ch->tables[tbl]; + KASSERT(th->ether_rnh != NULL, ("NULL IPFW table")); + th->ether_rnh->rnh_walktree(th->ether_rnh, flush_table_entry, th); return (0); } @@ -1908,7 +1996,12 @@ init_tables(struct ip_fw_chain *ch) uint16_t j; for (i = 0; i < IPFW_TABLES_MAX; i++) { - if (!rn_inithead((void **)&ch->tables[i], 32)) { + struct ipfw_table_head *th = &ch->tables[i]; + + if (!rn_inithead((void**)&(th->in_rnh), + __offsetof(struct table_entry_addr, in_addr) * 8) || + !rn_inithead((void**)&(th->ether_rnh), + __offsetof(struct table_entry_addr, ether_addr) * 8)) { for (j = 0; j < i; j++) { (void) flush_table(ch, j); } @@ -1920,18 +2013,34 @@ init_tables(struct ip_fw_chain *ch) static int lookup_table(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr, - uint32_t *val) + ipfw_ether_addr *ether_addr, uint32_t *val) { - struct radix_node_head *rnh; - struct table_entry *ent; - struct sockaddr_in sa; + struct ipfw_table_head *th; + struct table_entry_addr sa, mask; + struct table_entry *ent = NULL; + const int has_ether_addr = (ether_addr && (ether_addr->flags & IPFW_EA_CHECK)); + const int has_in_addr = (addr != INADDR_ANY); if (tbl >= IPFW_TABLES_MAX) return (0); - rnh = ch->tables[tbl]; - sa.sin_len = 8; - sa.sin_addr.s_addr = addr; - ent = (struct table_entry *)(rnh->rnh_lookup(&sa, NULL, rnh)); + th = &ch->tables[tbl]; + init_table_entry_addr(&sa, &mask, addr, (addr == INADDR_ANY ? 0 : 32), ether_addr); + if (has_ether_addr) { + ent = RN_TO_ENT(th->ether_rnh->rnh_lookup(&sa, NULL, th->ether_rnh), + ether_rn); + if (ent == NULL && has_in_addr) { + /* + * Try to lookup entry with any (zero) ether_addr. It's + * handled this way not to deal with non-continuous + * masks in radix trees. + */ + bzero(sa.ether_addr.octet, ETHER_ADDR_LEN); + ent = RN_TO_ENT(th->ether_rnh->rnh_lookup(&sa, NULL, th->ether_rnh), + ether_rn); + } + } else if (has_in_addr) { + ent = RN_TO_ENT(th->in_rnh->rnh_lookup(&sa, NULL, th->in_rnh), in_rn); + } if (ent != NULL) { *val = ent->value; return (1); @@ -1951,20 +2060,20 @@ count_table_entry(struct radix_node *rn, static int count_table(struct ip_fw_chain *ch, uint32_t tbl, uint32_t *cnt) { - struct radix_node_head *rnh; + struct ipfw_table_head *th; if (tbl >= IPFW_TABLES_MAX) return (EINVAL); - rnh = ch->tables[tbl]; + th = &ch->tables[tbl]; *cnt = 0; - rnh->rnh_walktree(rnh, count_table_entry, cnt); + th->ether_rnh->rnh_walktree(th->ether_rnh, count_table_entry, cnt); return (0); } static int dump_table_entry(struct radix_node *rn, void *arg) { - struct table_entry * const n = (struct table_entry *)rn; + struct table_entry * const n = RN_TO_ENT(rn, ether_rn); ipfw_table * const tbl = arg; ipfw_table_entry *ent; @@ -1972,11 +2081,23 @@ dump_table_entry(struct radix_node *rn, return (1); ent = &tbl->ent[tbl->cnt]; ent->tbl = tbl->tbl; - if (in_nullhost(n->mask.sin_addr)) + if (n->mask.in_addr == INADDR_ANY) ent->masklen = 0; else - ent->masklen = 33 - ffs(ntohl(n->mask.sin_addr.s_addr)); - ent->addr = n->addr.sin_addr.s_addr; + ent->masklen = 33 - ffs(ntohl(n->mask.in_addr)); + ent->addr = n->addr.in_addr; + memcpy(ent->ether_addr.octet, n->addr.ether_addr.octet, ETHER_ADDR_LEN); + ent->ether_addr.flags = 0; + +#define __ETHER_IS_ZERO(a) (((a)[0] | (a)[1] | (a)[2] | (a)[3] | (a)[4] | (a)[5]) == 0) + if (!__ETHER_IS_ZERO(n->mask.ether_addr.octet) && + !__ETHER_IS_ZERO(n->addr.ether_addr.octet)) { + ent->ether_addr.flags = IPFW_EA_CHECK; + /* Should be fixed after adding new flags */ + if (n->mask.ether_addr.octet[0] == 0x01) + ent->ether_addr.flags |= IPFW_EA_MULTICAST; + } +#undef __ETHER_IS_ZERO ent->value = n->value; tbl->cnt++; return (0); @@ -1985,13 +2106,13 @@ dump_table_entry(struct radix_node *rn, static int dump_table(struct ip_fw_chain *ch, ipfw_table *tbl) { - struct radix_node_head *rnh; + struct ipfw_table_head *th; if (tbl->tbl >= IPFW_TABLES_MAX) return (EINVAL); - rnh = ch->tables[tbl->tbl]; + th = &ch->tables[tbl->tbl]; tbl->cnt = 0; - rnh->rnh_walktree(rnh, dump_table_entry, tbl); + th->ether_rnh->rnh_walktree(th->ether_rnh, dump_table_entry, tbl); return (0); } @@ -2099,10 +2220,9 @@ check_uidgid(ipfw_insn_u32 *insn, int pr * Parameters: * * args->m (in/out) The packet; we set to NULL when/if we nuke it. - * Starts with the IP header. - * args->eh (in) Mac header if present, or NULL for layer3 packet. - * args->L3offset Number of bytes bypassed if we came from L2. - * e.g. often sizeof(eh) ** NOTYET ** + * Starts with the IP header or with layer2 header if IP_FW_ARGS_LAYER2 + * is set in args->flags. + * args->eh (in) ethernet header if present, or NULL for layer3 packet. * args->oif Outgoing interface, or NULL if packet is incoming. * The incoming interface is in the mbuf. (in) * args->divert_rule (in/out) @@ -2113,6 +2233,7 @@ check_uidgid(ipfw_insn_u32 *insn, int pr * 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->flags Flags * * Return value: * @@ -2139,10 +2260,8 @@ ipfw_chk(struct ip_fw_args *args) * the implementation of the various instructions to make sure * that they still work. * - * args->eh The MAC header. It is non-null for a layer2 + * args->eh The ethernet header. It is non-null for a layer2 * packet, it is NULL for a layer-3 packet. - * **notyet** - * args->L3offset Offset in the packet to the L3 (IP or equiv.) header. * * m | args->m Pointer to the mbuf, as received from the caller. * It may change if ipfw_chk() does an m_pullup, or if it @@ -2150,12 +2269,10 @@ ipfw_chk(struct ip_fw_args *args) * XXX This has to change, so that ipfw_chk() never modifies * or consumes the buffer. * ip is the beginning of the ip(4 or 6) header. - * Calculated by adding the L3offset to the start of data. - * (Until we start using L3offset, the packet is * supposed to start with the ip header). */ struct mbuf *m = args->m; - struct ip *ip = mtod(m, struct ip *); + struct ip *ip = NULL; /* * For rules which contain uid/gid or jail constraints, cache @@ -2251,6 +2368,9 @@ ipfw_chk(struct ip_fw_args *args) if (m->m_flags & M_SKIP_FIREWALL) return (IP_FW_PASS); /* accept */ + if ((args->flags & IP_FW_ARGS_LAYER2) == 0) + ip = mtod(m, struct ip *); + pktlen = m->m_pkthdr.len; args->f_id.fib = M_GETFIB(m); /* note mbuf not altered) */ proto = args->f_id.proto = 0; /* mark f_id invalid */ @@ -2276,12 +2396,22 @@ do { \ /* * if we have an ether header, */ - if (args->eh) + if (args->eh != NULL) { etype = ntohs(args->eh->ether_type); + memcpy(args->f_id.src_ether.octet, args->eh->ether_shost, + ETHER_ADDR_LEN); + args->f_id.src_ether.flags = IPFW_EA_CHECK; + memcpy(args->f_id.dst_ether.octet, args->eh->ether_dhost, + ETHER_ADDR_LEN); + args->f_id.dst_ether.flags = IPFW_EA_CHECK; + } else { + args->f_id.src_ether.flags = 0; + args->f_id.dst_ether.flags = 0; + } /* Identify IP packets and fill up variables. */ if (pktlen >= sizeof(struct ip6_hdr) && - (args->eh == NULL || etype == ETHERTYPE_IPV6) && ip->ip_v == 6) { + (args->flags & IP_FW_ARGS_LAYER2) == 0 && ip->ip_v == 6) { struct ip6_hdr *ip6 = (struct ip6_hdr *)ip; is_ipv6 = 1; args->f_id.addr_type = 6; @@ -2446,7 +2576,7 @@ do { \ args->f_id.dst_ip = 0; args->f_id.flow_id6 = ntohl(ip6->ip6_flow); } else if (pktlen >= sizeof(struct ip) && - (args->eh == NULL || etype == ETHERTYPE_IP) && ip->ip_v == 4) { + (args->flags & IP_FW_ARGS_LAYER2) == 0 && ip->ip_v == 4) { is_ipv4 = 1; hlen = ip->ip_hl << 2; args->f_id.addr_type = 4; @@ -2457,13 +2587,8 @@ do { \ proto = ip->ip_p; src_ip = ip->ip_src; dst_ip = ip->ip_dst; - if (args->eh != NULL) { /* layer 2 packets are as on the wire */ - offset = ntohs(ip->ip_off) & IP_OFFMASK; - ip_len = ntohs(ip->ip_len); - } else { - offset = ip->ip_off & IP_OFFMASK; - ip_len = ip->ip_len; - } + offset = ip->ip_off & IP_OFFMASK; + ip_len = ip->ip_len; pktlen = ip_len < pktlen ? ip_len : pktlen; if (offset == 0) { @@ -2494,6 +2619,13 @@ do { \ ip = mtod(m, struct ip *); args->f_id.src_ip = ntohl(src_ip.s_addr); args->f_id.dst_ip = ntohl(dst_ip.s_addr); + } else if (pktlen >= ETHER_HDR_LEN && args->eh != NULL && + (args->flags & IP_FW_ARGS_LAYER2)) { + void *hdr; + switch (ntohs(args->eh->ether_type)) { + case ETHERTYPE_ARP: + PULLUP_TO(ETHER_HDR_LEN, hdr, struct arphdr); + } } #undef PULLUP_TO if (proto) { /* we may have port numbers, store them */ @@ -2529,7 +2661,7 @@ do { \ int skipto = mtag ? divert_cookie(mtag) : 0; f = chain->rules; - if (args->eh == NULL && skipto != 0) { + if ((args->flags & IP_FW_ARGS_LAYER2) == 0 && skipto != 0) { if (skipto >= IPFW_DEFAULT_RULE) { IPFW_RUNLOCK(chain); return (IP_FW_DENY); /* invalid */ @@ -2555,6 +2687,7 @@ do { \ for (; f; f = f->next) { ipfw_insn *cmd; uint32_t tablearg = 0; + uint32_t stateopts = 0; int l, cmdlen, skip_or; /* skip rest of OR block */ again: @@ -2600,11 +2733,6 @@ check_body: match = 1; break; - case O_FORWARD_MAC: - printf("ipfw: opcode %d unimplemented\n", - cmd->opcode); - break; - case O_GID: case O_UID: case O_JAIL: @@ -2641,23 +2769,20 @@ check_body: m->m_pkthdr.rcvif, (ipfw_insn_if *)cmd); break; - case O_MACADDR2: - if (args->eh != NULL) { /* have MAC header */ - u_int32_t *want = (u_int32_t *) - ((ipfw_insn_mac *)cmd)->addr; - u_int32_t *mask = (u_int32_t *) - ((ipfw_insn_mac *)cmd)->mask; - u_int32_t *hdr = (u_int32_t *)args->eh; - - match = - ( want[0] == (hdr[0] & mask[0]) && - want[1] == (hdr[1] & mask[1]) && - want[2] == (hdr[2] & mask[2]) ); + case O_ETHER_SRC: + case O_ETHER_DST: + if (args->eh != NULL) { /* have ethernet header */ + ipfw_ether_addr *want = + &(((ipfw_insn_ether *)cmd)->ether); + ipfw_ether_addr *a = (cmd->opcode == O_ETHER_SRC ? + &args->f_id.src_ether : + &args->f_id.dst_ether); + match = ether_addr_allow(want, a); } break; - case O_MAC_TYPE: - if (args->eh != NULL) { + case O_ETHER_TYPE: + if (args->eh != NULL) { /* have ethernet header */ u_int16_t *p = ((ipfw_insn_u16 *)cmd)->ports; int i; @@ -2678,7 +2803,7 @@ check_body: break; case O_LAYER2: - match = (args->eh != NULL); + match = ((args->flags & IP_FW_ARGS_LAYER2) != 0); *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200901251925.n0PJPfEu025860>