diff --git a/sys/conf/options b/sys/conf/options index f52b390de0f..48e0c90efad 100644 --- a/sys/conf/options +++ b/sys/conf/options @@ -969,6 +969,9 @@ RACCT_DEFAULT_TO_DISABLED opt_global.h # Resource Limits RCTL opt_global.h +# OPNsense additions +PF_SHARE_FORWARD opt_pf.h + # Random number generator(s) # Which CSPRNG hash we get. # If Yarrow is not chosen, Fortuna is selected. diff --git a/sys/netinet/ip_fastfwd.c b/sys/netinet/ip_fastfwd.c index c0c618680b2..df943c5b6c9 100644 --- a/sys/netinet/ip_fastfwd.c +++ b/sys/netinet/ip_fastfwd.c @@ -154,7 +154,6 @@ ip_tryforward(struct mbuf *m) struct in_addr odest, dest; uint16_t ip_len, ip_off; int error = 0; - struct m_tag *fwd_tag = NULL; /* * Are we active and forwarding packets? @@ -317,9 +316,7 @@ passin: /* * Destination address changed? */ - if (m->m_flags & M_IP_NEXTHOP) - fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL); - if (odest.s_addr != dest.s_addr || fwd_tag != NULL) { + if (odest.s_addr != dest.s_addr || IP_HAS_NEXTHOP(m)) { /* * Is it now for a local address on this host? */ @@ -334,11 +331,13 @@ forwardlocal: /* * Redo route lookup with new destination address */ - if (fwd_tag) { - dest.s_addr = ((struct sockaddr_in *) - (fwd_tag + 1))->sin_addr.s_addr; - m_tag_delete(m, fwd_tag); - m->m_flags &= ~M_IP_NEXTHOP; + if (IP_HAS_NEXTHOP(m)) { + struct sockaddr_in tmp; + if (ip_get_fwdtag(m, &tmp, NULL)) { + return (NULL); + } + dest.s_addr = tmp.sin_addr.s_addr; + ip_flush_fwdtag(m); } if (ip_findroute(&nh, dest, m) != 0) return (NULL); /* icmp unreach already sent */ diff --git a/sys/netinet/ip_input.c b/sys/netinet/ip_input.c index cfa0bf34ef8..799a0fd011f 100644 --- a/sys/netinet/ip_input.c +++ b/sys/netinet/ip_input.c @@ -622,16 +622,14 @@ tooshort: goto ours; } reinjected: - if (m->m_flags & M_IP_NEXTHOP) { - if (m_tag_find(m, PACKET_TAG_IPFORWARD, NULL) != NULL) { - /* - * Directly ship the packet on. This allows - * forwarding packets originally destined to us - * to some other directly connected host. - */ - ip_forward(m, 1); - return; - } + if (IP_HAS_NEXTHOP(m)) { + /* + * Directly ship the packet on. This allows + * forwarding packets originally destined to us + * to some other directly connected host. + */ + ip_forward(m, 1); + return; } passin: diff --git a/sys/netinet/ip_output.c b/sys/netinet/ip_output.c index da56300b1e3..c8f528b682b 100644 --- a/sys/netinet/ip_output.c +++ b/sys/netinet/ip_output.c @@ -102,20 +102,20 @@ extern int in_mcast_loop; extern struct protosw inetsw[]; static inline int -ip_output_pfil(struct mbuf **mp, struct ifnet *ifp, struct inpcb *inp, +ip_output_pfil(struct mbuf **mp, struct ifnet **ifp, struct inpcb *inp, struct sockaddr_in *dst, int *fibnum, int *error) { - struct m_tag *fwd_tag = NULL; struct mbuf *m; struct in_addr odst; struct ip *ip; + u_short ifidx; m = *mp; ip = mtod(m, struct ip *); /* Run through list of hooks for output packets. */ odst.s_addr = ip->ip_dst.s_addr; - *error = pfil_run_hooks(&V_inet_pfil_hook, mp, ifp, PFIL_OUT, inp); + *error = pfil_run_hooks(&V_inet_pfil_hook, mp, *ifp, PFIL_OUT, inp); m = *mp; if ((*error) != 0 || m == NULL) return 1; /* Finished */ @@ -179,13 +179,15 @@ ip_output_pfil(struct mbuf **mp, struct ifnet *ifp, struct inpcb *inp, return 1; /* Finished */ } /* Or forward to some other address? */ - if ((m->m_flags & M_IP_NEXTHOP) && - ((fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL)) != NULL)) { - bcopy((fwd_tag+1), dst, sizeof(struct sockaddr_in)); + if (IP_HAS_NEXTHOP(m) && !ip_get_fwdtag(m, dst, &ifidx)) { + if (ifidx != 0) { + struct ifnet *nifp = ifnet_byindex(ifidx); + if (nifp != NULL) { + *ifp = nifp; + } + } m->m_flags |= M_SKIP_FIREWALL; - m->m_flags &= ~M_IP_NEXTHOP; - m_tag_delete(m, fwd_tag); - + ip_flush_fwdtag(m); return -1; /* Reloop for CHANGE of dst */ } @@ -403,6 +405,7 @@ again: isbroadcast = in_broadcast(gw->sin_addr, ifp); } +haveroute: /* * Calculate MTU. If we have a route that is up, use that, * otherwise use the interface's MTU. @@ -571,7 +574,8 @@ sendit: /* Jump over all PFIL processing if hooks are not active. */ if (PFIL_HOOKED(&V_inet_pfil_hook)) { - switch (ip_output_pfil(&m, ifp, inp, dst, &fibnum, &error)) { + struct ifnet *same = ifp; + switch (ip_output_pfil(&m, &ifp, inp, dst, &fibnum, &error)) { case 1: /* Finished */ goto done; @@ -588,6 +592,9 @@ sendit: rte = NULL; gw = dst; ip = mtod(m, struct ip *); + if (same != ifp) { + goto haveroute; + } goto again; } @@ -1390,3 +1397,89 @@ ip_mloopback(struct ifnet *ifp, const struct mbuf *m, int hlen) if_simloop(ifp, copym, AF_INET, 0); } } + +struct ip_fwdtag { + struct sockaddr_in dst; + u_short if_index; +}; + +int +ip_set_fwdtag(struct mbuf *m, struct sockaddr_in *dst, u_short ifidx) +{ + struct ip_fwdtag *fwd_info; + struct m_tag *fwd_tag; + + KASSERT(dst != NULL, ("%s: !dst", __func__)); + KASSERT(dst->sin_family == AF_INET, ("%s: !AF_INET", __func__)); + + fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL); + if (fwd_tag != NULL) { + KASSERT(((struct ip_fwdtag *)(fwd_tag+1))->dst.sin_family == + AF_INET, ("%s: !AF_INET", __func__)); + + m_tag_unlink(m, fwd_tag); + } else { + fwd_tag = m_tag_get(PACKET_TAG_IPFORWARD, sizeof(*fwd_info), + M_NOWAIT); + if (fwd_tag == NULL) { + return (ENOBUFS); + } + } + + fwd_info = (struct ip_fwdtag *)(fwd_tag+1); + + bcopy(dst, &fwd_info->dst, sizeof(fwd_info->dst)); + fwd_info->if_index = ifidx; + m->m_flags |= M_IP_NEXTHOP; + + if (in_localip(fwd_info->dst.sin_addr)) + m->m_flags |= M_FASTFWD_OURS; + else + m->m_flags &= ~M_FASTFWD_OURS; + + m_tag_prepend(m, fwd_tag); + + return (0); +} + +int +ip_get_fwdtag(struct mbuf *m, struct sockaddr_in *dst, u_short *ifidx) +{ + struct ip_fwdtag *fwd_info; + struct m_tag *fwd_tag; + + fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL); + if (fwd_tag == NULL) { + return (ENOENT); + } + + fwd_info = (struct ip_fwdtag *)(fwd_tag+1); + + KASSERT(((struct sockaddr *)&fwd_info->dst)->sa_family == AF_INET, + ("%s: !AF_INET", __func__)); + + if (dst != NULL) { + bcopy(&fwd_info->dst, dst, sizeof(*dst)); + } + + if (ifidx != NULL) { + *ifidx = fwd_info->if_index; + } + + return (0); +} + +void +ip_flush_fwdtag(struct mbuf *m) +{ + struct m_tag *fwd_tag; + + fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL); + if (fwd_tag != NULL) { + KASSERT(((struct sockaddr *)(fwd_tag+1))->sa_family == + AF_INET, ("%s: !AF_INET", __func__)); + + m->m_flags &= ~(M_IP_NEXTHOP | M_FASTFWD_OURS); + m_tag_delete(m, fwd_tag); + } +} diff --git a/sys/netinet/ip_var.h b/sys/netinet/ip_var.h index 847704fd1d7..46caa75cb02 100644 --- a/sys/netinet/ip_var.h +++ b/sys/netinet/ip_var.h @@ -236,6 +236,11 @@ extern int (*ip_rsvp_vif)(struct socket *, struct sockopt *); extern void (*ip_rsvp_force_done)(struct socket *); extern int (*rsvp_input_p)(struct mbuf **, int *, int); +#define IP_HAS_NEXTHOP(m) ((m)->m_flags & M_IP_NEXTHOP) +int ip_set_fwdtag(struct mbuf *, struct sockaddr_in *, u_short); +int ip_get_fwdtag(struct mbuf *, struct sockaddr_in *, u_short *); +void ip_flush_fwdtag(struct mbuf *); + VNET_DECLARE(struct pfil_head, inet_pfil_hook); /* packet filter hooks */ #define V_inet_pfil_hook VNET(inet_pfil_hook) diff --git a/sys/netinet/tcp_input.c b/sys/netinet/tcp_input.c index 69fc6cb0e49..395ca57455c 100644 --- a/sys/netinet/tcp_input.c +++ b/sys/netinet/tcp_input.c @@ -575,6 +575,7 @@ tcp_input(struct mbuf **mp, int *offp, int proto) int off0; int optlen = 0; #ifdef INET + struct sockaddr_in next_hop; int len; #endif int tlen = 0, off; @@ -582,8 +583,8 @@ tcp_input(struct mbuf **mp, int *offp, int proto) int thflags; int rstreason = 0; /* For badport_bandlim accounting purposes */ uint8_t iptos; - struct m_tag *fwd_tag = NULL; #ifdef INET6 + struct sockaddr_in6 next_hop6; struct ip6_hdr *ip6 = NULL; int isipv6; #else @@ -777,22 +778,6 @@ tcp_input(struct mbuf **mp, int *offp, int proto) } else ti_locked = TI_UNLOCKED; - /* - * Grab info from PACKET_TAG_IPFORWARD tag prepended to the chain. - */ - if ( -#ifdef INET6 - (isipv6 && (m->m_flags & M_IP6_NEXTHOP)) -#ifdef INET - || (!isipv6 && (m->m_flags & M_IP_NEXTHOP)) -#endif -#endif -#if defined(INET) && !defined(INET6) - (m->m_flags & M_IP_NEXTHOP) -#endif - ) - fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL); - findpcb: #ifdef INVARIANTS if (ti_locked == TI_RLOCKED) { @@ -802,10 +787,11 @@ findpcb: } #endif #ifdef INET6 - if (isipv6 && fwd_tag != NULL) { - struct sockaddr_in6 *next_hop6; - - next_hop6 = (struct sockaddr_in6 *)(fwd_tag + 1); + /* + * Grab info from IP forward tag prepended to the chain. + */ + if (isipv6 && IP6_HAS_NEXTHOP(m) && + !ip6_get_fwdtag(m, &next_hop6, NULL)) { /* * Transparently forwarded. Pretend to be the destination. * Already got one like this? @@ -820,11 +806,13 @@ findpcb: * any hardware-generated hash is ignored. */ inp = in6_pcblookup(&V_tcbinfo, &ip6->ip6_src, - th->th_sport, &next_hop6->sin6_addr, - next_hop6->sin6_port ? ntohs(next_hop6->sin6_port) : + th->th_sport, &next_hop6.sin6_addr, + next_hop6.sin6_port ? ntohs(next_hop6.sin6_port) : th->th_dport, INPLOOKUP_WILDCARD | INPLOOKUP_WLOCKPCB, m->m_pkthdr.rcvif); } + /* Remove the tag from the packet. We don't need it anymore. */ + ip6_flush_fwdtag(m); } else if (isipv6) { inp = in6_pcblookup_mbuf(&V_tcbinfo, &ip6->ip6_src, th->th_sport, &ip6->ip6_dst, th->th_dport, @@ -836,10 +824,10 @@ findpcb: else #endif #ifdef INET - if (fwd_tag != NULL) { - struct sockaddr_in *next_hop; - - next_hop = (struct sockaddr_in *)(fwd_tag+1); + /* + * Grab info from IP forward tag prepended to the chain. + */ + if (IP_HAS_NEXTHOP(m) && !ip_get_fwdtag(m, &next_hop, NULL)) { /* * Transparently forwarded. Pretend to be the destination. * already got one like this? @@ -854,11 +842,13 @@ findpcb: * any hardware-generated hash is ignored. */ inp = in_pcblookup(&V_tcbinfo, ip->ip_src, - th->th_sport, next_hop->sin_addr, - next_hop->sin_port ? ntohs(next_hop->sin_port) : + th->th_sport, next_hop.sin_addr, + next_hop.sin_port ? ntohs(next_hop.sin_port) : th->th_dport, INPLOOKUP_WILDCARD | INPLOOKUP_WLOCKPCB, m->m_pkthdr.rcvif); } + /* Remove the tag from the packet. We don't need it anymore. */ + ip_flush_fwdtag(m); } else inp = in_pcblookup_mbuf(&V_tcbinfo, ip->ip_src, th->th_sport, ip->ip_dst, th->th_dport, diff --git a/sys/netinet/udp_usrreq.c b/sys/netinet/udp_usrreq.c index c771201ae77..e014d399148 100644 --- a/sys/netinet/udp_usrreq.c +++ b/sys/netinet/udp_usrreq.c @@ -386,7 +386,7 @@ udp_input(struct mbuf **mp, int *offp, int proto) struct ip save_ip; struct sockaddr_in udp_in; struct mbuf *m; - struct m_tag *fwd_tag; + struct sockaddr_in next_hop; int cscov_partial, iphlen; m = *mp; @@ -624,14 +624,9 @@ udp_input(struct mbuf **mp, int *offp, int proto) */ /* - * Grab info from PACKET_TAG_IPFORWARD tag prepended to the chain. + * Grab info from IP forward tag prepended to the chain. */ - if ((m->m_flags & M_IP_NEXTHOP) && - (fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL)) != NULL) { - struct sockaddr_in *next_hop; - - next_hop = (struct sockaddr_in *)(fwd_tag + 1); - + if (IP_HAS_NEXTHOP(m) && !ip_get_fwdtag(m, &next_hop, NULL)) { /* * Transparently forwarded. Pretend to be the destination. * Already got one like this? @@ -645,14 +640,13 @@ udp_input(struct mbuf **mp, int *offp, int proto) * any hardware-generated hash is ignored. */ inp = in_pcblookup(pcbinfo, ip->ip_src, - uh->uh_sport, next_hop->sin_addr, - next_hop->sin_port ? htons(next_hop->sin_port) : + uh->uh_sport, next_hop.sin_addr, + next_hop.sin_port ? htons(next_hop.sin_port) : uh->uh_dport, INPLOOKUP_WILDCARD | INPLOOKUP_RLOCKPCB, ifp); } /* Remove the tag from the packet. We don't need it anymore. */ - m_tag_delete(m, fwd_tag); - m->m_flags &= ~M_IP_NEXTHOP; + ip_flush_fwdtag(m); } else inp = in_pcblookup_mbuf(pcbinfo, ip->ip_src, uh->uh_sport, ip->ip_dst, uh->uh_dport, INPLOOKUP_WILDCARD | diff --git a/sys/netinet6/ip6_fastfwd.c b/sys/netinet6/ip6_fastfwd.c index 5358868b68a..82f50ee34ea 100644 --- a/sys/netinet6/ip6_fastfwd.c +++ b/sys/netinet6/ip6_fastfwd.c @@ -87,7 +87,6 @@ ip6_tryforward(struct mbuf *m) { struct sockaddr_in6 dst; struct nhop6_basic nh; - struct m_tag *fwd_tag; struct ip6_hdr *ip6; struct ifnet *rcvif; uint32_t plen; @@ -173,14 +172,16 @@ ip6_tryforward(struct mbuf *m) return (m); ip6 = mtod(m, struct ip6_hdr *); - if ((m->m_flags & M_IP6_NEXTHOP) && - (fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL)) != NULL) { + if (IP6_HAS_NEXTHOP(m)) { /* * Now we will find route to forwarded by pfil destination. */ - bcopy((fwd_tag + 1), &dst, sizeof(dst)); - m->m_flags &= ~M_IP6_NEXTHOP; - m_tag_delete(m, fwd_tag); + struct sockaddr_in6 tmp; + if (ip6_get_fwdtag(m, &tmp, NULL)) { + return (NULL); + } + dst.sin6_addr = tmp.sin6_addr; + ip6_flush_fwdtag(m); } else { /* Update dst since pfil could change it */ dst.sin6_addr = ip6->ip6_dst; @@ -232,17 +233,15 @@ passin: * Again. A packet filter could change the destination address. */ ip6 = mtod(m, struct ip6_hdr *); - if (m->m_flags & M_IP6_NEXTHOP) - fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL); - else - fwd_tag = NULL; - - if (fwd_tag != NULL || + if (IP6_HAS_NEXTHOP(m) || !IN6_ARE_ADDR_EQUAL(&dst.sin6_addr, &ip6->ip6_dst)) { - if (fwd_tag != NULL) { - bcopy((fwd_tag + 1), &dst, sizeof(dst)); - m->m_flags &= ~M_IP6_NEXTHOP; - m_tag_delete(m, fwd_tag); + if (IP6_HAS_NEXTHOP(m)) { + struct sockaddr_in6 tmp; + if (ip6_get_fwdtag(m, &tmp, NULL)) { + return (NULL); + } + dst.sin6_addr = tmp.sin6_addr; + ip6_flush_fwdtag(m); } else dst.sin6_addr = ip6->ip6_dst; /* diff --git a/sys/netinet6/ip6_forward.c b/sys/netinet6/ip6_forward.c index bf1f66e4248..d5e1047decb 100644 --- a/sys/netinet6/ip6_forward.c +++ b/sys/netinet6/ip6_forward.c @@ -98,7 +98,6 @@ ip6_forward(struct mbuf *m, int srcrt) #ifdef SCTP int sw_csum; #endif - struct m_tag *fwd_tag; char ip6bufs[INET6_ADDRSTRLEN], ip6bufd[INET6_ADDRSTRLEN]; /* @@ -359,13 +358,17 @@ again2: goto out; } /* Or forward to some other address? */ - if ((m->m_flags & M_IP6_NEXTHOP) && - (fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL)) != NULL) { + if (IP6_HAS_NEXTHOP(m)) { dst = (struct sockaddr_in6 *)&rin6.ro_dst; - bcopy((fwd_tag+1), dst, sizeof(struct sockaddr_in6)); + if (ip6_get_fwdtag(m, dst, NULL) != 0) { + if (mcopy) { + m_freem(m); + goto freecopy; + } + goto bad; + } m->m_flags |= M_SKIP_FIREWALL; - m->m_flags &= ~M_IP6_NEXTHOP; - m_tag_delete(m, fwd_tag); + ip6_flush_fwdtag(m); RTFREE(rt); goto again2; } diff --git a/sys/netinet6/ip6_input.c b/sys/netinet6/ip6_input.c index 4f6b5b1816a..83fe32ed368 100644 --- a/sys/netinet6/ip6_input.c +++ b/sys/netinet6/ip6_input.c @@ -785,8 +785,7 @@ ip6_input(struct mbuf *m) goto hbhcheck; } reinjected: - if ((m->m_flags & M_IP6_NEXTHOP) && - m_tag_find(m, PACKET_TAG_IPFORWARD, NULL) != NULL) { + if (IP6_HAS_NEXTHOP(m)) { /* * Directly ship the packet on. This allows forwarding * packets originally destined to us to some other directly diff --git a/sys/netinet6/ip6_output.c b/sys/netinet6/ip6_output.c index 551f1747b75..5ef15a3b3f8 100644 --- a/sys/netinet6/ip6_output.c +++ b/sys/netinet6/ip6_output.c @@ -319,8 +319,8 @@ ip6_output(struct mbuf *m0, struct ip6_pktopts *opt, int hdrsplit = 0; int sw_csum, tso; int needfiblookup; + int has_fwd_tag = 0; uint32_t fibnum; - struct m_tag *fwd_tag = NULL; uint32_t id; if (inp != NULL) { @@ -541,7 +541,7 @@ again: ro->ro_dst.sin6_family = AF_INET6; RT_VALIDATE((struct route *)ro, &inp->inp_rt_cookie, fibnum); } - if (ro->ro_rt && fwd_tag == NULL && (ro->ro_rt->rt_flags & RTF_UP) && + if (ro->ro_rt && !has_fwd_tag && (ro->ro_rt->rt_flags & RTF_UP) && ro->ro_dst.sin6_family == AF_INET6 && IN6_ARE_ADDR_EQUAL(&ro->ro_dst.sin6_addr, &ip6->ip6_dst)) { rt = ro->ro_rt; @@ -550,7 +550,7 @@ again: if (ro->ro_lle) LLE_FREE(ro->ro_lle); /* zeros ro_lle */ ro->ro_lle = NULL; - if (fwd_tag == NULL) { + if (!has_fwd_tag) { bzero(&dst_sa, sizeof(dst_sa)); dst_sa.sin6_family = AF_INET6; dst_sa.sin6_len = sizeof(dst_sa); @@ -854,13 +854,11 @@ again: goto done; } /* Or forward to some other address? */ - if ((m->m_flags & M_IP6_NEXTHOP) && - (fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL)) != NULL) { + if (IP6_HAS_NEXTHOP(m) && !ip6_get_fwdtag(m, &dst_sa, NULL)) { dst = (struct sockaddr_in6 *)&ro->ro_dst; - bcopy((fwd_tag+1), &dst_sa, sizeof(struct sockaddr_in6)); m->m_flags |= M_SKIP_FIREWALL; - m->m_flags &= ~M_IP6_NEXTHOP; - m_tag_delete(m, fwd_tag); + ip6_flush_fwdtag(m); + has_fwd_tag = 1; goto again; } @@ -3062,3 +3060,102 @@ ip6_optlen(struct inpcb *in6p) return len; #undef elen } + +struct ip6_fwdtag { + struct sockaddr_in6 dst; + u_short if_index; +}; + +int +ip6_set_fwdtag(struct mbuf *m, struct sockaddr_in6 *dst, u_short ifidx) +{ + struct ip6_fwdtag *fwd_info; + struct m_tag *fwd_tag; + int error; + + KASSERT(dst != NULL, ("%s: !dst", __func__)); + KASSERT(dst->sin6_family == AF_INET6, ("%s: !AF_INET6", __func__)); + + fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL); + if (fwd_tag != NULL) { + KASSERT(((struct ip6_fwdtag *)(fwd_tag+1))->dst.sin6_family == + AF_INET6, ("%s: !AF_INET6", __func__)); + + m_tag_unlink(m, fwd_tag); + } else { + fwd_tag = m_tag_get(PACKET_TAG_IPFORWARD, sizeof(*dst), + M_NOWAIT); + if (fwd_tag == NULL) { + return (ENOBUFS); + } + } + + fwd_info = (struct ip6_fwdtag *)(fwd_tag+1); + + bcopy(dst, &fwd_info->dst, sizeof(fwd_info->dst)); + + /* + * If nh6 address is link-local we should convert + * it to kernel internal form before doing any + * comparisons. + */ + error = sa6_embedscope(&fwd_info->dst, V_ip6_use_defzone); + if (error != 0) { + m_tag_free(fwd_tag); + return (error); + } + + if (in6_localip(&fwd_info->dst.sin6_addr)) + m->m_flags |= M_FASTFWD_OURS; + else + m->m_flags &= ~M_FASTFWD_OURS; + + fwd_info->if_index = ifidx; + m->m_flags |= M_IP6_NEXTHOP; + + m_tag_prepend(m, fwd_tag); + + return (0); +} + +int +ip6_get_fwdtag(struct mbuf *m, struct sockaddr_in6 *dst, u_short *ifidx) +{ + struct ip6_fwdtag *fwd_info; + struct m_tag *fwd_tag; + + fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL); + if (fwd_tag == NULL) { + return (ENOENT); + } + + fwd_info = (struct ip6_fwdtag *)(fwd_tag+1); + + KASSERT(((struct sockaddr *)&fwd_info->dst)->sa_family == AF_INET6, + ("%s: !AF_INET6", __func__)); + + if (dst != NULL) { + bcopy((fwd_tag+1), dst, sizeof(*dst)); + } + + if (ifidx != NULL) { + *ifidx = fwd_info->if_index; + } + + return (0); +} + +void +ip6_flush_fwdtag(struct mbuf *m) +{ + struct m_tag *fwd_tag; + + fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL); + if (fwd_tag != NULL) { + KASSERT(((struct sockaddr *)(fwd_tag+1))->sa_family == + AF_INET6, ("%s: !AF_INET6", __func__)); + + m->m_flags &= ~(M_IP6_NEXTHOP | M_FASTFWD_OURS); + m_tag_delete(m, fwd_tag); + } +} diff --git a/sys/netinet6/ip6_var.h b/sys/netinet6/ip6_var.h index e52a3206856..5e60677208a 100644 --- a/sys/netinet6/ip6_var.h +++ b/sys/netinet6/ip6_var.h @@ -397,6 +397,11 @@ int ip6_deletefraghdr(struct mbuf *, int, int); int ip6_fragment(struct ifnet *, struct mbuf *, int, u_char, int, uint32_t); +#define IP6_HAS_NEXTHOP(m) ((m)->m_flags & M_IP6_NEXTHOP) +int ip6_set_fwdtag(struct mbuf *, struct sockaddr_in6 *, u_short); +int ip6_get_fwdtag(struct mbuf *, struct sockaddr_in6 *, u_short *); +void ip6_flush_fwdtag(struct mbuf *); + int route6_input(struct mbuf **, int *, int); void frag6_init(void); diff --git a/sys/netinet6/udp6_usrreq.c b/sys/netinet6/udp6_usrreq.c index 56faf5a8515..70e6414dbf6 100644 --- a/sys/netinet6/udp6_usrreq.c +++ b/sys/netinet6/udp6_usrreq.c @@ -203,7 +203,7 @@ udp6_input(struct mbuf **mp, int *offp, int proto) int cscov_partial; int plen, ulen; struct sockaddr_in6 fromsa; - struct m_tag *fwd_tag; + struct sockaddr_in6 next_hop6; uint16_t uh_sum; uint8_t nxt; @@ -411,14 +411,9 @@ udp6_input(struct mbuf **mp, int *offp, int proto) */ /* - * Grab info from PACKET_TAG_IPFORWARD tag prepended to the chain. + * Grab info from IP forward tag prepended to the chain. */ - if ((m->m_flags & M_IP6_NEXTHOP) && - (fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL)) != NULL) { - struct sockaddr_in6 *next_hop6; - - next_hop6 = (struct sockaddr_in6 *)(fwd_tag + 1); - + if (IP6_HAS_NEXTHOP(m) && !ip6_get_fwdtag(m, &next_hop6, NULL)) { /* * Transparently forwarded. Pretend to be the destination. * Already got one like this? @@ -433,14 +428,13 @@ udp6_input(struct mbuf **mp, int *offp, int proto) * any hardware-generated hash is ignored. */ inp = in6_pcblookup(pcbinfo, &ip6->ip6_src, - uh->uh_sport, &next_hop6->sin6_addr, - next_hop6->sin6_port ? htons(next_hop6->sin6_port) : + uh->uh_sport, &next_hop6.sin6_addr, + next_hop6.sin6_port ? htons(next_hop6.sin6_port) : uh->uh_dport, INPLOOKUP_WILDCARD | INPLOOKUP_RLOCKPCB, m->m_pkthdr.rcvif); } /* Remove the tag from the packet. We don't need it anymore. */ - m_tag_delete(m, fwd_tag); - m->m_flags &= ~M_IP6_NEXTHOP; + ip6_flush_fwdtag(m); } else inp = in6_pcblookup_mbuf(pcbinfo, &ip6->ip6_src, uh->uh_sport, &ip6->ip6_dst, uh->uh_dport, diff --git a/sys/netpfil/ipfw/ip_fw_pfil.c b/sys/netpfil/ipfw/ip_fw_pfil.c index 4316526df80..dd7071fd52e 100644 --- a/sys/netpfil/ipfw/ip_fw_pfil.c +++ b/sys/netpfil/ipfw/ip_fw_pfil.c @@ -165,66 +165,25 @@ again: ret = EACCES; #else { - struct m_tag *fwd_tag; - 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 - * reuse the tag if present. - */ - fwd_tag = (dir == DIR_IN) ? NULL : - m_tag_find(*m0, PACKET_TAG_IPFORWARD, NULL); - if (fwd_tag != NULL) { - m_tag_unlink(*m0, fwd_tag); - } else { - fwd_tag = m_tag_get(PACKET_TAG_IPFORWARD, len, - M_NOWAIT); - if (fwd_tag == NULL) { - ret = EACCES; - break; /* i.e. drop */ - } - } #ifdef INET6 if (args.next_hop6 != NULL) { - struct sockaddr_in6 *sa6; - - sa6 = (struct sockaddr_in6 *)(fwd_tag + 1); - bcopy(args.next_hop6, sa6, len); - /* - * If nh6 address is link-local we should convert - * it to kernel internal form before doing any - * comparisons. - */ - if (sa6_embedscope(sa6, V_ip6_use_defzone) != 0) { + if (ip6_set_fwdtag(*m0, args.next_hop6, 0)) { ret = EACCES; break; } - if (in6_localip(&sa6->sin6_addr)) - (*m0)->m_flags |= M_FASTFWD_OURS; - (*m0)->m_flags |= M_IP6_NEXTHOP; } #endif #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; + if (ip_set_fwdtag(*m0, args.next_hop, 0)) { + ret = EACCES; + break; + } } #endif - m_tag_prepend(*m0, fwd_tag); } #endif /* INET || INET6 */ break; diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c index 1fa0b7a5a93..0cd090e8feb 100644 --- a/sys/netpfil/pf/pf.c +++ b/sys/netpfil/pf/pf.c @@ -290,6 +290,9 @@ static void pf_mtag_free(struct m_tag *); static void pf_route(struct mbuf **, struct pf_rule *, int, struct ifnet *, struct pf_state *, struct pf_pdesc *); +static void pf_route_shared(struct mbuf **, struct pf_rule *, int, + struct ifnet *, struct pf_state *, + struct pf_pdesc *); #endif /* INET */ #ifdef INET6 static void pf_change_a6(struct pf_addr *, u_int16_t *, @@ -297,6 +300,9 @@ static void pf_change_a6(struct pf_addr *, u_int16_t *, static void pf_route6(struct mbuf **, struct pf_rule *, int, struct ifnet *, struct pf_state *, struct pf_pdesc *); +static void pf_route6_shared(struct mbuf **, struct pf_rule *, + int, struct ifnet *, struct pf_state *, + struct pf_pdesc *); #endif /* INET6 */ int in4_cksum(struct mbuf *m, u_int8_t nxt, int off, int len); @@ -368,6 +374,25 @@ SYSCTL_ULONG(_net_pf, OID_AUTO, states_hashsize, CTLFLAG_RDTUN, SYSCTL_ULONG(_net_pf, OID_AUTO, source_nodes_hashsize, CTLFLAG_RDTUN, &pf_srchashsize, 0, "Size of pf(4) source nodes hashtable"); +#ifdef PF_SHARE_FORWARD +static VNET_DEFINE(int, pf_share_forward) = 1; +static VNET_DEFINE(int, pf_share_forward6) = 1; +#else +static VNET_DEFINE(int, pf_share_forward) = 0; +static VNET_DEFINE(int, pf_share_forward6) = 0; +#endif + +#define V_pf_share_forward VNET(pf_share_forward) +#define V_pf_share_forward6 VNET(pf_share_forward6) + +SYSCTL_INT(_net_pf, OID_AUTO, share_forward, + CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(pf_share_forward), 0, + "If set pf(4) will share IPv4 forwarding decisions with ipfw(4)."); + +SYSCTL_INT(_net_pf, OID_AUTO, share_forward6, + CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(pf_share_forward6), 0, + "If set pf(4) will share IPv6 forwarding decisions with ipfw(4)."); + VNET_DEFINE(void *, pf_swi_cookie); VNET_DEFINE(uint32_t, pf_hashseed); @@ -5568,6 +5593,113 @@ done: *m = NULL; return; +bad_locked: + if (s) + PF_STATE_UNLOCK(s); +bad: + m_freem(m0); + goto done; +} + +static void +pf_route_shared(struct mbuf **m, struct pf_rule *r, int dir, struct ifnet *ifp, + struct pf_state *s, struct pf_pdesc *pd) +{ + struct mbuf *m0; + struct sockaddr_in dst; + struct ip *ip; + struct pf_addr naddr; + struct pf_src_node *sn = NULL; + int error = 0; + + KASSERT(m && *m && r && ifp, ("%s: invalid parameters", __func__)); + KASSERT(dir == PF_IN || dir == PF_OUT, ("%s: invalid direction", + __func__)); + + if ((pd->pf_mtag == NULL && + ((pd->pf_mtag = pf_get_mtag(*m)) == NULL)) || + pd->pf_mtag->routed++ > 3) { + m0 = *m; + *m = NULL; + goto bad_locked; + } + + if (r->rt == PF_DUPTO) { + if ((m0 = m_dup(*m, M_NOWAIT)) == NULL) { + if (s) + PF_STATE_UNLOCK(s); + return; + } + } else { + if ((r->rt == PF_REPLYTO) == (r->direction == dir)) { + if (s) + PF_STATE_UNLOCK(s); + return; + } + m0 = *m; + } + + /* retain old behaviour by avoiding a rewrite */ + if (IP_HAS_NEXTHOP(m0)) { + if (s) + PF_STATE_UNLOCK(s); + return; + } + + ip = mtod(m0, struct ip *); + + bzero(&dst, sizeof(dst)); + dst.sin_family = AF_INET; + dst.sin_len = sizeof(dst); + dst.sin_addr = ip->ip_dst; + + if (r->rt == PF_FASTROUTE) { + struct nhop4_basic nh4; + + if (s) + PF_STATE_UNLOCK(s); + + if (fib4_lookup_nh_basic(M_GETFIB(m0), ip->ip_dst, 0, + m0->m_pkthdr.flowid, &nh4) != 0) { + KMOD_IPSTAT_INC(ips_noroute); + error = EHOSTUNREACH; + goto bad; + } + + ifp = nh4.nh_ifp; + dst.sin_addr = nh4.nh_addr; + } else { + if (TAILQ_EMPTY(&r->rpool.list)) { + DPFPRINTF(PF_DEBUG_URGENT, + ("%s: TAILQ_EMPTY(&r->rpool.list)\n", __func__)); + goto bad_locked; + } + if (s == NULL) { + pf_map_addr(AF_INET, r, (struct pf_addr *)&ip->ip_src, + &naddr, NULL, &sn); + if (!PF_AZERO(&naddr, AF_INET)) + dst.sin_addr.s_addr = naddr.v4.s_addr; + ifp = r->rpool.cur->kif ? + r->rpool.cur->kif->pfik_ifp : NULL; + } else { + if (!PF_AZERO(&s->rt_addr, AF_INET)) + dst.sin_addr.s_addr = + s->rt_addr.v4.s_addr; + ifp = s->rt_kif ? s->rt_kif->pfik_ifp : NULL; + PF_STATE_UNLOCK(s); + } + } + if (ifp == NULL) + goto bad; + + if (ip_set_fwdtag(m0, &dst, ifp->if_index)) + goto bad; + +done: + if (r->rt == PF_DUPTO && IP_HAS_NEXTHOP(m0)) + ip_forward(m0, 1); + return; + bad_locked: if (s) PF_STATE_UNLOCK(s); @@ -5703,6 +5835,109 @@ done: *m = NULL; return; +bad_locked: + if (s) + PF_STATE_UNLOCK(s); +bad: + m_freem(m0); + goto done; +} + +static void +pf_route6_shared(struct mbuf **m, struct pf_rule *r, int dir, + struct ifnet *ifp, struct pf_state *s, struct pf_pdesc *pd) +{ + struct mbuf *m0; + struct sockaddr_in6 dst; + struct ip6_hdr *ip6; + struct pf_addr naddr; + struct pf_src_node *sn = NULL; + + KASSERT(m && *m && r && ifp, ("%s: invalid parameters", __func__)); + KASSERT(dir == PF_IN || dir == PF_OUT, ("%s: invalid direction", + __func__)); + + if ((pd->pf_mtag == NULL && + ((pd->pf_mtag = pf_get_mtag(*m)) == NULL)) || + pd->pf_mtag->routed++ > 3) { + m0 = *m; + *m = NULL; + goto bad_locked; + } + + if (r->rt == PF_DUPTO) { + if ((m0 = m_dup(*m, M_NOWAIT)) == NULL) { + if (s) + PF_STATE_UNLOCK(s); + return; + } + } else { + if ((r->rt == PF_REPLYTO) == (r->direction == dir)) { + if (s) + PF_STATE_UNLOCK(s); + return; + } + m0 = *m; + } + + /* retain old behaviour by avoiding a rewrite */ + if (IP6_HAS_NEXTHOP(m0)) { + if (s) + PF_STATE_UNLOCK(s); + return; + } + + ip6 = mtod(m0, struct ip6_hdr *); + + bzero(&dst, sizeof(dst)); + dst.sin6_family = AF_INET6; + dst.sin6_len = sizeof(dst); + dst.sin6_addr = ip6->ip6_dst; + + /* Cheat. XXX why only in the v6 case??? */ + if (r->rt == PF_FASTROUTE) { + if (s) + PF_STATE_UNLOCK(s); + m0->m_flags |= M_SKIP_FIREWALL; + ip6_output(m0, NULL, NULL, 0, NULL, NULL, NULL); + *m = NULL; + return; + } + + if (TAILQ_EMPTY(&r->rpool.list)) { + DPFPRINTF(PF_DEBUG_URGENT, + ("%s: TAILQ_EMPTY(&r->rpool.list)\n", __func__)); + goto bad_locked; + } + if (s == NULL) { + pf_map_addr(AF_INET6, r, (struct pf_addr *)&ip6->ip6_src, + &naddr, NULL, &sn); + if (!PF_AZERO(&naddr, AF_INET6)) + PF_ACPY((struct pf_addr *)&dst.sin6_addr, + &naddr, AF_INET6); + ifp = r->rpool.cur->kif ? r->rpool.cur->kif->pfik_ifp : NULL; + } else { + if (!PF_AZERO(&s->rt_addr, AF_INET6)) + PF_ACPY((struct pf_addr *)&dst.sin6_addr, + &s->rt_addr, AF_INET6); + ifp = s->rt_kif ? s->rt_kif->pfik_ifp : NULL; + } + + if (s) + PF_STATE_UNLOCK(s); + + if (ifp == NULL) + goto bad; + + if (ip6_set_fwdtag(m0, &dst, ifp->if_index)) + goto bad; + +done: + if (r->rt == PF_DUPTO && IP6_HAS_NEXTHOP(m0)) + ip6_forward(m0, 1); + + return; + bad_locked: if (s) PF_STATE_UNLOCK(s); @@ -5861,12 +6096,25 @@ pf_test(int dir, struct ifnet *ifp, struct mbuf **m0, struct inpcb *inp) struct pf_ruleset *ruleset = NULL; struct pf_pdesc pd; int off, dirndx, pqid = 0; + int share_forward = V_pf_share_forward; + u_short ifidx; M_ASSERTPKTHDR(m); if (!V_pf_status.running) return (PF_PASS); + /* restore the correct forwarding interface */ + if (share_forward && dir == PF_OUT && IP_HAS_NEXTHOP(m) && + !ip_get_fwdtag(m, NULL, &ifidx)) { + if (ifidx != 0) { + struct ifnet *nifp = ifnet_byindex(ifidx); + if (nifp != NULL) { + ifp = nifp; + } + } + } + memset(&pd, 0, sizeof(pd)); kif = (struct pfi_kif *)ifp->if_pf_kif; @@ -6222,7 +6470,11 @@ done: default: /* pf_route() returns unlocked. */ if (r->rt) { - pf_route(m0, r, dir, kif->pfik_ifp, s, &pd); + if (!share_forward) + pf_route(m0, r, dir, kif->pfik_ifp, s, &pd); + else + pf_route_shared(m0, r, dir, kif->pfik_ifp, s, + &pd); return (action); } break; @@ -6249,6 +6501,8 @@ pf_test6(int dir, struct ifnet *ifp, struct mbuf **m0, struct inpcb *inp) struct pf_pdesc pd; int off, terminal = 0, dirndx, rh_cnt = 0, pqid = 0; int fwdir = dir; + int share_forward = V_pf_share_forward6; + u_short ifidx; M_ASSERTPKTHDR(m); @@ -6273,6 +6527,17 @@ pf_test6(int dir, struct ifnet *ifp, struct mbuf **m0, struct inpcb *inp) if (!V_pf_status.running) return (PF_PASS); + /* restore the correct forwarding interface */ + if (share_forward && dir == PF_OUT && IP6_HAS_NEXTHOP(m) && + !ip6_get_fwdtag(m, NULL, &ifidx)) { + if (ifidx != 0) { + struct ifnet *nifp = ifnet_byindex(ifidx); + if (nifp != NULL) { + ifp = nifp; + } + } + } + memset(&pd, 0, sizeof(pd)); pd.pf_mtag = pf_find_mtag(m); @@ -6637,7 +6902,11 @@ done: default: /* pf_route6() returns unlocked. */ if (r->rt) { - pf_route6(m0, r, dir, kif->pfik_ifp, s, &pd); + if (!share_forward) + pf_route6(m0, r, dir, kif->pfik_ifp, s, &pd); + else + pf_route6_shared(m0, r, dir, kif->pfik_ifp, s, + &pd); return (action); } break;