Convert IP/IPv6 forwarding, ICMP processing and IP PCB laddr selection to

the new routing KPI.

Reviewed by:	ae
Differential Revision:	https://reviews.freebsd.org/D24245
This commit is contained in:
Alexander V. Chernikov 2020-04-14 23:06:25 +00:00
parent fb8ed4c5f8
commit 9ac7c6cfed
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=359942
7 changed files with 87 additions and 84 deletions

View File

@ -693,7 +693,7 @@ void kmod_icmp6stat_inc(int statnum);
#ifdef _KERNEL
# ifdef __STDC__
struct rtentry;
struct nhop_object;
struct rttimer;
struct in6_multi;
# endif
@ -705,7 +705,7 @@ void icmp6_fasttimo(void);
void icmp6_slowtimo(void);
void icmp6_prepare(struct mbuf *);
void icmp6_redirect_input(struct mbuf *, int);
void icmp6_redirect_output(struct mbuf *, struct rtentry *);
void icmp6_redirect_output(struct mbuf *, struct nhop_object *);
struct ip6ctlparam;
void icmp6_mtudisc_update(struct ip6ctlparam *, int);

View File

@ -88,6 +88,7 @@ __FBSDID("$FreeBSD$");
#include <netinet/in_pcb.h>
#ifdef INET
#include <netinet/in_var.h>
#include <netinet/in_fib.h>
#endif
#include <netinet/ip_var.h>
#include <netinet/tcp_var.h>
@ -102,6 +103,7 @@ __FBSDID("$FreeBSD$");
#include <netinet6/in6_var.h>
#include <netinet6/ip6_var.h>
#endif /* INET6 */
#include <net/route/nhop.h>
#endif
#include <netipsec/ipsec_support.h>
@ -1033,8 +1035,8 @@ in_pcbladdr(struct inpcb *inp, struct in_addr *faddr, struct in_addr *laddr,
{
struct ifaddr *ifa;
struct sockaddr *sa;
struct sockaddr_in *sin;
struct route sro;
struct sockaddr_in *sin, dst;
struct nhop_object *nh;
int error;
NET_EPOCH_ASSERT();
@ -1047,9 +1049,10 @@ in_pcbladdr(struct inpcb *inp, struct in_addr *faddr, struct in_addr *laddr,
return (0);
error = 0;
bzero(&sro, sizeof(sro));
sin = (struct sockaddr_in *)&sro.ro_dst;
nh = NULL;
bzero(&dst, sizeof(dst));
sin = &dst;
sin->sin_family = AF_INET;
sin->sin_len = sizeof(struct sockaddr_in);
sin->sin_addr.s_addr = faddr->s_addr;
@ -1061,7 +1064,8 @@ in_pcbladdr(struct inpcb *inp, struct in_addr *faddr, struct in_addr *laddr,
* Find out route to destination.
*/
if ((inp->inp_socket->so_options & SO_DONTROUTE) == 0)
in_rtalloc_ign(&sro, 0, inp->inp_inc.inc_fibnum);
nh = fib4_lookup(inp->inp_inc.inc_fibnum, *faddr,
0, NHR_NONE, 0);
/*
* If we found a route, use the address corresponding to
@ -1071,7 +1075,7 @@ in_pcbladdr(struct inpcb *inp, struct in_addr *faddr, struct in_addr *laddr,
* network and try to find a corresponding interface to take
* the source address from.
*/
if (sro.ro_rt == NULL || sro.ro_rt->rt_ifp == NULL) {
if (nh == NULL || nh->nh_ifp == NULL) {
struct in_ifaddr *ia;
struct ifnet *ifp;
@ -1124,22 +1128,22 @@ in_pcbladdr(struct inpcb *inp, struct in_addr *faddr, struct in_addr *laddr,
* belonging to this jail. If so use it.
* 3. as a last resort return the 'default' jail address.
*/
if ((sro.ro_rt->rt_ifp->if_flags & IFF_LOOPBACK) == 0) {
if ((nh->nh_ifp->if_flags & IFF_LOOPBACK) == 0) {
struct in_ifaddr *ia;
struct ifnet *ifp;
/* If not jailed, use the default returned. */
if (cred == NULL || !prison_flag(cred, PR_IP4)) {
ia = (struct in_ifaddr *)sro.ro_rt->rt_ifa;
ia = (struct in_ifaddr *)nh->nh_ifa;
laddr->s_addr = ia->ia_addr.sin_addr.s_addr;
goto done;
}
/* Jailed. */
/* 1. Check if the iface address belongs to the jail. */
sin = (struct sockaddr_in *)sro.ro_rt->rt_ifa->ifa_addr;
sin = (struct sockaddr_in *)nh->nh_ifa->ifa_addr;
if (prison_check_ip4(cred, &sin->sin_addr) == 0) {
ia = (struct in_ifaddr *)sro.ro_rt->rt_ifa;
ia = (struct in_ifaddr *)nh->nh_ifa;
laddr->s_addr = ia->ia_addr.sin_addr.s_addr;
goto done;
}
@ -1149,7 +1153,7 @@ in_pcbladdr(struct inpcb *inp, struct in_addr *faddr, struct in_addr *laddr,
* belonging to this jail.
*/
ia = NULL;
ifp = sro.ro_rt->rt_ifp;
ifp = nh->nh_ifp;
CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
sa = ifa->ifa_addr;
if (sa->sa_family != AF_INET)
@ -1179,22 +1183,16 @@ in_pcbladdr(struct inpcb *inp, struct in_addr *faddr, struct in_addr *laddr,
* In case of jails, check that it is an address of the jail
* and if we cannot find, fall back to the 'default' jail address.
*/
if ((sro.ro_rt->rt_ifp->if_flags & IFF_LOOPBACK) != 0) {
struct sockaddr_in sain;
if ((nh->nh_ifp->if_flags & IFF_LOOPBACK) != 0) {
struct in_ifaddr *ia;
bzero(&sain, sizeof(struct sockaddr_in));
sain.sin_family = AF_INET;
sain.sin_len = sizeof(struct sockaddr_in);
sain.sin_addr.s_addr = faddr->s_addr;
ia = ifatoia(ifa_ifwithdstaddr(sintosa(&sain),
ia = ifatoia(ifa_ifwithdstaddr(sintosa(&dst),
inp->inp_socket->so_fibnum));
if (ia == NULL)
ia = ifatoia(ifa_ifwithnet(sintosa(&sain), 0,
ia = ifatoia(ifa_ifwithnet(sintosa(&dst), 0,
inp->inp_socket->so_fibnum));
if (ia == NULL)
ia = ifatoia(ifa_ifwithaddr(sintosa(&sain)));
ia = ifatoia(ifa_ifwithaddr(sintosa(&dst)));
if (cred == NULL || !prison_flag(cred, PR_IP4)) {
if (ia == NULL) {
@ -1234,8 +1232,6 @@ in_pcbladdr(struct inpcb *inp, struct in_addr *faddr, struct in_addr *laddr,
}
done:
if (sro.ro_rt != NULL)
RTFREE(sro.ro_rt);
return (error);
}

View File

@ -96,6 +96,7 @@ __FBSDID("$FreeBSD$");
#include <net/if_dl.h>
#include <net/pfil.h>
#include <net/route.h>
#include <net/route/nhop.h>
#include <net/vnet.h>
#include <netinet/in.h>
@ -111,11 +112,13 @@ __FBSDID("$FreeBSD$");
#include <machine/in_cksum.h>
static int
ip_findroute(struct nhop4_basic *pnh, struct in_addr dest, struct mbuf *m)
ip_findroute(struct nhop_object **pnh, struct in_addr dest, struct mbuf *m)
{
struct nhop_object *nh;
bzero(pnh, sizeof(*pnh));
if (fib4_lookup_nh_basic(M_GETFIB(m), dest, 0, 0, pnh) != 0) {
nh = fib4_lookup(M_GETFIB(m), dest, 0, NHR_NONE,
m->m_pkthdr.flowid);
if (nh == NULL) {
IPSTAT_INC(ips_noroute);
IPSTAT_INC(ips_cantforward);
icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_HOST, 0, 0);
@ -124,18 +127,20 @@ ip_findroute(struct nhop4_basic *pnh, struct in_addr dest, struct mbuf *m)
/*
* Drop blackholed traffic and directed broadcasts.
*/
if ((pnh->nh_flags & (NHF_BLACKHOLE | NHF_BROADCAST)) != 0) {
if ((nh->nh_flags & (NHF_BLACKHOLE | NHF_BROADCAST)) != 0) {
IPSTAT_INC(ips_cantforward);
m_freem(m);
return (EHOSTUNREACH);
}
if (pnh->nh_flags & NHF_REJECT) {
if (nh->nh_flags & NHF_REJECT) {
IPSTAT_INC(ips_cantforward);
icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_HOST, 0, 0);
return (EHOSTUNREACH);
}
*pnh = nh;
return (0);
}
@ -151,7 +156,7 @@ ip_tryforward(struct mbuf *m)
{
struct ip *ip;
struct mbuf *m0 = NULL;
struct nhop4_basic nh;
struct nhop_object *nh;
struct sockaddr_in dst;
struct in_addr dest, odest, rtdest;
uint16_t ip_len, ip_off;
@ -323,7 +328,7 @@ ip_tryforward(struct mbuf *m)
if (!PFIL_HOOKED_OUT(V_inet_pfil_head))
goto passout;
if (pfil_run_hooks(V_inet_pfil_head, &m, nh.nh_ifp,
if (pfil_run_hooks(V_inet_pfil_head, &m, nh->nh_ifp,
PFIL_OUT | PFIL_FWD, NULL) != PFIL_PASS)
goto drop;
@ -376,12 +381,15 @@ ip_tryforward(struct mbuf *m)
bzero(&dst, sizeof(dst));
dst.sin_family = AF_INET;
dst.sin_len = sizeof(dst);
dst.sin_addr = nh.nh_addr;
if (nh->nh_flags & NHF_GATEWAY)
dst.sin_addr = nh->gw4_sa.sin_addr;
else
dst.sin_addr = dest;
/*
* Check if packet fits MTU or if hardware will fragment for us
*/
if (ip_len <= nh.nh_mtu) {
if (ip_len <= nh->nh_mtu) {
/*
* Avoid confusing lower layers.
*/
@ -389,8 +397,8 @@ ip_tryforward(struct mbuf *m)
/*
* Send off the packet via outgoing interface
*/
IP_PROBE(send, NULL, NULL, ip, nh.nh_ifp, ip, NULL);
error = (*nh.nh_ifp->if_output)(nh.nh_ifp, m,
IP_PROBE(send, NULL, NULL, ip, nh->nh_ifp, ip, NULL);
error = (*nh->nh_ifp->if_output)(nh->nh_ifp, m,
(struct sockaddr *)&dst, NULL);
} else {
/*
@ -399,15 +407,15 @@ ip_tryforward(struct mbuf *m)
if (ip_off & IP_DF) {
IPSTAT_INC(ips_cantfrag);
icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_NEEDFRAG,
0, nh.nh_mtu);
0, nh->nh_mtu);
goto consumed;
} else {
/*
* We have to fragment the packet
*/
m->m_pkthdr.csum_flags |= CSUM_IP;
if (ip_fragment(ip, &m, nh.nh_mtu,
nh.nh_ifp->if_hwassist) != 0)
if (ip_fragment(ip, &m, nh->nh_mtu,
nh->nh_ifp->if_hwassist) != 0)
goto drop;
KASSERT(m != NULL, ("null mbuf and no error"));
/*
@ -423,10 +431,9 @@ ip_tryforward(struct mbuf *m)
m_clrprotoflags(m);
IP_PROBE(send, NULL, NULL,
mtod(m, struct ip *), nh.nh_ifp,
mtod(m, struct ip *), nh->nh_ifp,
mtod(m, struct ip *), NULL);
/* XXX: we can use cached route here */
error = (*nh.nh_ifp->if_output)(nh.nh_ifp, m,
error = (*nh->nh_ifp->if_output)(nh->nh_ifp, m,
(struct sockaddr *)&dst, NULL);
if (error)
break;

View File

@ -52,6 +52,7 @@ __FBSDID("$FreeBSD$");
#include <net/if_var.h>
#include <net/if_types.h>
#include <net/route.h>
#include <net/route/nhop.h>
#include <net/vnet.h>
#include <netinet/in.h>
@ -945,7 +946,7 @@ static int
icmp_verify_redirect_gateway(struct sockaddr_in *src, struct sockaddr_in *dst,
struct sockaddr_in *gateway, u_int fibnum)
{
struct rtentry *rt;
struct nhop_object *nh;
struct ifaddr *ifa;
NET_EPOCH_ASSERT();
@ -958,8 +959,8 @@ icmp_verify_redirect_gateway(struct sockaddr_in *src, struct sockaddr_in *dst,
if (ifa_ifwithaddr_check((struct sockaddr *)gateway))
return (EHOSTUNREACH);
rt = rtalloc1_fib((struct sockaddr *)dst, 0, 0UL, fibnum); /* NB: rt is locked */
if (rt == NULL)
nh = fib4_lookup(fibnum, dst->sin_addr, 0, NHR_NONE, 0);
if (nh == NULL)
return (EINVAL);
/*
@ -968,28 +969,19 @@ icmp_verify_redirect_gateway(struct sockaddr_in *src, struct sockaddr_in *dst,
* we have a routing loop, perhaps as a result of an interface
* going down recently.
*/
if (!sa_equal((struct sockaddr *)src, rt->rt_gateway)) {
RTFREE_LOCKED(rt);
if (!sa_equal((struct sockaddr *)src, &nh->gw_sa))
return (EINVAL);
}
if (rt->rt_ifa != ifa && ifa->ifa_addr->sa_family != AF_LINK) {
RTFREE_LOCKED(rt);
if (nh->nh_ifa != ifa && ifa->ifa_addr->sa_family != AF_LINK)
return (EINVAL);
}
/* If host route already exists, ignore redirect. */
if (rt->rt_flags & RTF_HOST) {
RTFREE_LOCKED(rt);
if (nh->nh_flags & NHF_HOST)
return (EEXIST);
}
/* If the prefix is directly reachable, ignore redirect. */
if (!(rt->rt_flags & RTF_GATEWAY)) {
RTFREE_LOCKED(rt);
if (!(nh->nh_flags & NHF_GATEWAY))
return (EEXIST);
}
RTFREE_LOCKED(rt);
return (0);
}

View File

@ -93,6 +93,7 @@ __FBSDID("$FreeBSD$");
#include <net/if_llatbl.h>
#include <net/if_types.h>
#include <net/route.h>
#include <net/route/nhop.h>
#include <net/vnet.h>
#include <netinet/in.h>
@ -2412,7 +2413,7 @@ icmp6_redirect_input(struct mbuf *m, int off)
}
void
icmp6_redirect_output(struct mbuf *m0, struct rtentry *rt)
icmp6_redirect_output(struct mbuf *m0, struct nhop_object *nh)
{
struct ifnet *ifp; /* my outgoing interface */
struct in6_addr *ifp_ll6;
@ -2435,7 +2436,7 @@ icmp6_redirect_output(struct mbuf *m0, struct rtentry *rt)
goto fail;
/* sanity check */
if (!m0 || !rt || !(rt->rt_flags & RTF_UP) || !(ifp = rt->rt_ifp))
if (!m0 || !nh || !(NH_IS_VALID(nh)) || !(ifp = nh->nh_ifp))
goto fail;
/*
@ -2469,7 +2470,7 @@ icmp6_redirect_output(struct mbuf *m0, struct rtentry *rt)
m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
if (m == NULL)
goto fail;
M_SETFIB(m, rt->rt_fibnum);
M_SETFIB(m, M_GETFIB(m0));
maxlen = M_TRAILINGSPACE(m);
maxlen = min(IPV6_MMTU, maxlen);
/* just for safety */
@ -2491,9 +2492,9 @@ icmp6_redirect_output(struct mbuf *m0, struct rtentry *rt)
}
/* get ip6 linklocal address for the router. */
if (rt->rt_gateway && (rt->rt_flags & RTF_GATEWAY)) {
if (nh->nh_flags & NHF_GATEWAY) {
struct sockaddr_in6 *sin6;
sin6 = (struct sockaddr_in6 *)rt->rt_gateway;
sin6 = &nh->gw6_sa;
router_ll6 = &sin6->sin6_addr;
if (!IN6_IS_ADDR_LINKLOCAL(router_ll6))
router_ll6 = (struct in6_addr *)NULL;
@ -2517,7 +2518,7 @@ icmp6_redirect_output(struct mbuf *m0, struct rtentry *rt)
nd_rd->nd_rd_type = ND_REDIRECT;
nd_rd->nd_rd_code = 0;
nd_rd->nd_rd_reserved = 0;
if (rt->rt_flags & RTF_GATEWAY) {
if (nh->nh_flags & NHF_GATEWAY) {
/*
* nd_rd->nd_rd_target must be a link-local address in
* better router cases.

View File

@ -40,6 +40,7 @@ __FBSDID("$FreeBSD$");
#include <net/if.h>
#include <net/if_var.h>
#include <net/route.h>
#include <net/route/nhop.h>
#include <net/pfil.h>
#include <net/vnet.h>
@ -55,30 +56,35 @@ __FBSDID("$FreeBSD$");
#include <netinet6/nd6.h>
static int
ip6_findroute(struct nhop6_basic *pnh, const struct sockaddr_in6 *dst,
ip6_findroute(struct nhop_object **pnh, const struct sockaddr_in6 *dst,
struct mbuf *m)
{
struct nhop_object *nh;
if (fib6_lookup_nh_basic(M_GETFIB(m), &dst->sin6_addr,
dst->sin6_scope_id, 0, dst->sin6_flowinfo, pnh) != 0) {
nh = fib6_lookup(M_GETFIB(m), &dst->sin6_addr,
dst->sin6_scope_id, NHR_NONE, m->m_pkthdr.flowid);
if (nh == NULL) {
IP6STAT_INC(ip6s_noroute);
IP6STAT_INC(ip6s_cantforward);
icmp6_error(m, ICMP6_DST_UNREACH,
ICMP6_DST_UNREACH_NOROUTE, 0);
return (EHOSTUNREACH);
}
if (pnh->nh_flags & NHF_BLACKHOLE) {
if (nh->nh_flags & NHF_BLACKHOLE) {
IP6STAT_INC(ip6s_cantforward);
m_freem(m);
return (EHOSTUNREACH);
}
if (pnh->nh_flags & NHF_REJECT) {
if (nh->nh_flags & NHF_REJECT) {
IP6STAT_INC(ip6s_cantforward);
icmp6_error(m, ICMP6_DST_UNREACH,
ICMP6_DST_UNREACH_REJECT, 0);
return (EHOSTUNREACH);
}
*pnh = nh;
return (0);
}
@ -86,7 +92,7 @@ struct mbuf*
ip6_tryforward(struct mbuf *m)
{
struct sockaddr_in6 dst;
struct nhop6_basic nh;
struct nhop_object *nh;
struct m_tag *fwd_tag;
struct ip6_hdr *ip6;
struct ifnet *rcvif;
@ -196,9 +202,9 @@ ip6_tryforward(struct mbuf *m)
goto dropin;
}
if (!PFIL_HOOKED_OUT(V_inet6_pfil_head)) {
if (m->m_pkthdr.len > nh.nh_mtu) {
in6_ifstat_inc(nh.nh_ifp, ifs6_in_toobig);
icmp6_error(m, ICMP6_PACKET_TOO_BIG, 0, nh.nh_mtu);
if (m->m_pkthdr.len > nh->nh_mtu) {
in6_ifstat_inc(nh->nh_ifp, ifs6_in_toobig);
icmp6_error(m, ICMP6_PACKET_TOO_BIG, 0, nh->nh_mtu);
m = NULL;
goto dropout;
}
@ -208,7 +214,7 @@ ip6_tryforward(struct mbuf *m)
/*
* Outgoing packet firewall processing.
*/
if (pfil_run_hooks(V_inet6_pfil_head, &m, nh.nh_ifp, PFIL_OUT |
if (pfil_run_hooks(V_inet6_pfil_head, &m, nh->nh_ifp, PFIL_OUT |
PFIL_FWD, NULL) != PFIL_PASS)
goto dropout;
@ -216,9 +222,9 @@ ip6_tryforward(struct mbuf *m)
* We used slow path processing for packets with scoped addresses.
* So, scope checks aren't needed here.
*/
if (m->m_pkthdr.len > nh.nh_mtu) {
in6_ifstat_inc(nh.nh_ifp, ifs6_in_toobig);
icmp6_error(m, ICMP6_PACKET_TOO_BIG, 0, nh.nh_mtu);
if (m->m_pkthdr.len > nh->nh_mtu) {
in6_ifstat_inc(nh->nh_ifp, ifs6_in_toobig);
icmp6_error(m, ICMP6_PACKET_TOO_BIG, 0, nh->nh_mtu);
m = NULL;
goto dropout;
}
@ -272,16 +278,17 @@ ip6_tryforward(struct mbuf *m)
}
m_clrprotoflags(m); /* Avoid confusing lower layers. */
IP_PROBE(send, NULL, NULL, ip6, nh.nh_ifp, NULL, ip6);
IP_PROBE(send, NULL, NULL, ip6, nh->nh_ifp, NULL, ip6);
dst.sin6_addr = nh.nh_addr;
error = (*nh.nh_ifp->if_output)(nh.nh_ifp, m,
if (nh->nh_flags & NHF_GATEWAY)
dst.sin6_addr = nh->gw6_sa.sin6_addr;
error = (*nh->nh_ifp->if_output)(nh->nh_ifp, m,
(struct sockaddr *)&dst, NULL);
if (error != 0) {
in6_ifstat_inc(nh.nh_ifp, ifs6_out_discard);
in6_ifstat_inc(nh->nh_ifp, ifs6_out_discard);
IP6STAT_INC(ip6s_cantforward);
} else {
in6_ifstat_inc(nh.nh_ifp, ifs6_out_forward);
in6_ifstat_inc(nh->nh_ifp, ifs6_out_forward);
IP6STAT_INC(ip6s_forward);
}
return (NULL);
@ -289,7 +296,7 @@ ip6_tryforward(struct mbuf *m)
in6_ifstat_inc(rcvif, ifs6_in_discard);
goto drop;
dropout:
in6_ifstat_inc(nh.nh_ifp, ifs6_out_discard);
in6_ifstat_inc(nh->nh_ifp, ifs6_out_discard);
drop:
if (m != NULL)
m_freem(m);

View File

@ -401,7 +401,7 @@ ip6_forward(struct mbuf *m, int srcrt)
switch (error) {
case 0:
if (type == ND_REDIRECT) {
icmp6_redirect_output(mcopy, rt);
icmp6_redirect_output(mcopy, rt->rt_nhop);
goto out;
}
goto freecopy;