summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/net/route.h9
-rw-r--r--sys/netinet6/in6.h2
-rw-r--r--sys/netinet6/ip6_output.c126
3 files changed, 101 insertions, 36 deletions
diff --git a/sys/net/route.h b/sys/net/route.h
index 743c830..847722c 100644
--- a/sys/net/route.h
+++ b/sys/net/route.h
@@ -44,16 +44,17 @@
*/
/*
- * A route consists of a destination address, a reference
- * to a routing entry, and a reference to an llentry.
- * These are often held by protocols in their control
- * blocks, e.g. inpcb.
+ * Struct route consiste of a destination address,
+ * a route entry pointer, link-layer prepend data pointer along
+ * with its length.
*/
struct route {
struct rtentry *ro_rt;
char *ro_prepend;
uint16_t ro_plen;
uint16_t ro_flags;
+ uint16_t ro_mtu; /* saved ro_rt mtu */
+ uint16_t spare;
struct sockaddr ro_dst;
};
diff --git a/sys/netinet6/in6.h b/sys/netinet6/in6.h
index 9bc142a..ae049f0 100644
--- a/sys/netinet6/in6.h
+++ b/sys/netinet6/in6.h
@@ -378,6 +378,8 @@ struct route_in6 {
char *ro_prepend;
uint16_t ro_plen;
uint16_t ro_flags;
+ uint16_t ro_mtu; /* saved ro_rt mtu */
+ uint16_t spare;
struct sockaddr_in6 ro_dst;
};
#endif
diff --git a/sys/netinet6/ip6_output.c b/sys/netinet6/ip6_output.c
index af9fdba..16e860b 100644
--- a/sys/netinet6/ip6_output.c
+++ b/sys/netinet6/ip6_output.c
@@ -147,8 +147,11 @@ static int ip6_insertfraghdr(struct mbuf *, struct mbuf *, int,
struct ip6_frag **);
static int ip6_insert_jumboopt(struct ip6_exthdrs *, u_int32_t);
static int ip6_splithdr(struct mbuf *, struct ip6_exthdrs *);
-static int ip6_getpmtu(struct route_in6 *, struct route_in6 *,
+static int ip6_getpmtu(struct route_in6 *, int,
struct ifnet *, struct in6_addr *, u_long *, int *, u_int);
+static int ip6_calcmtu(struct ifnet *, const struct in6_addr *, u_long,
+ u_long *, int *);
+static int ip6_getpmtu_ctl(u_int, struct in6_addr *, u_long *);
static int copypktopts(struct ip6_pktopts *, struct ip6_pktopts *, int);
@@ -712,7 +715,7 @@ again:
*ifpp = ifp;
/* Determine path MTU. */
- if ((error = ip6_getpmtu(ro_pmtu, ro, ifp, &finaldst, &mtu,
+ if ((error = ip6_getpmtu(ro_pmtu, ro != ro_pmtu, ifp, &finaldst, &mtu,
&alwaysfrag, fibnum)) != 0)
goto bad;
@@ -1045,8 +1048,6 @@ sendorfree:
done:
if (ro == &ip6route)
RO_RTFREE(ro);
- if (ro_pmtu == &ip6route)
- RO_RTFREE(ro_pmtu);
return (error);
freehdrs:
@@ -1215,35 +1216,104 @@ ip6_insertfraghdr(struct mbuf *m0, struct mbuf *m, int hlen,
return (0);
}
+/*
+ * Calculates IPv6 path mtu for destination @dst.
+ * Resulting MTU is stored in @mtup.
+ *
+ * Returns 0 on success.
+ */
static int
-ip6_getpmtu(struct route_in6 *ro_pmtu, struct route_in6 *ro,
+ip6_getpmtu_ctl(u_int fibnum, struct in6_addr *dst, u_long *mtup)
+{
+ struct route_in6 ro_pmtu;
+ struct ifnet *ifp;
+ struct sockaddr_in6 *sa6_dst;
+ u_long mtu;
+
+ sa6_dst = (struct sockaddr_in6 *)&ro_pmtu.ro_dst;
+ bzero(sa6_dst, sizeof(*sa6_dst));
+ sa6_dst->sin6_family = AF_INET6;
+ sa6_dst->sin6_len = sizeof(struct sockaddr_in6);
+ sa6_dst->sin6_addr = *dst;
+
+ in6_rtalloc(&ro_pmtu, fibnum);
+
+ if (ro_pmtu.ro_rt == NULL)
+ return (EHOSTUNREACH);
+
+ ifp = ro_pmtu.ro_rt->rt_ifp;
+ mtu = ro_pmtu.ro_rt->rt_mtu;
+ RO_RTFREE(&ro_pmtu);
+
+ return (ip6_calcmtu(ifp, dst, mtu, mtup, NULL));
+}
+
+/*
+ * Calculates IPv6 path MTU for @dst based on transmit @ifp,
+ * and cached data in @ro_pmtu.
+ * MTU from (successful) route lookup is saved (along with dst)
+ * inside @ro_pmtu to avoid subsequent route lookups after packet
+ * filter processing.
+ *
+ * Stores mtu and always-frag value into @mtup and @alwaysfragp.
+ * Returns 0 on success.
+ */
+static int
+ip6_getpmtu(struct route_in6 *ro_pmtu, int do_lookup,
struct ifnet *ifp, struct in6_addr *dst, u_long *mtup,
int *alwaysfragp, u_int fibnum)
{
- u_int32_t mtu = 0;
- int alwaysfrag = 0;
- int error = 0;
+ struct sockaddr_in6 *sa6_dst;
+ u_long mtu;
- if (ro_pmtu != ro) {
- /* The first hop and the final destination may differ. */
- struct sockaddr_in6 *sa6_dst =
- (struct sockaddr_in6 *)&ro_pmtu->ro_dst;
- if (ro_pmtu->ro_rt &&
- ((ro_pmtu->ro_rt->rt_flags & RTF_UP) == 0 ||
- !IN6_ARE_ADDR_EQUAL(&sa6_dst->sin6_addr, dst))) {
- RTFREE(ro_pmtu->ro_rt);
- ro_pmtu->ro_rt = (struct rtentry *)NULL;
- }
- if (ro_pmtu->ro_rt == NULL) {
+ mtu = 0;
+ if (do_lookup) {
+
+ /*
+ * Here ro_pmtu has final destination address, while
+ * ro might represent immediate destination.
+ * Use ro_pmtu destination since mtu might differ.
+ */
+ sa6_dst = (struct sockaddr_in6 *)&ro_pmtu->ro_dst;
+ if (!IN6_ARE_ADDR_EQUAL(&sa6_dst->sin6_addr, dst))
+ ro_pmtu->ro_mtu = 0;
+
+ if (ro_pmtu->ro_mtu == 0) {
bzero(sa6_dst, sizeof(*sa6_dst));
sa6_dst->sin6_family = AF_INET6;
sa6_dst->sin6_len = sizeof(struct sockaddr_in6);
sa6_dst->sin6_addr = *dst;
in6_rtalloc(ro_pmtu, fibnum);
+ if (ro_pmtu->ro_rt) {
+ mtu = ro_pmtu->ro_rt->rt_mtu;
+ RO_RTFREE(ro_pmtu);
+ }
}
}
- if (ro_pmtu->ro_rt) {
+
+ if (ro_pmtu->ro_rt)
+ mtu = ro_pmtu->ro_rt->rt_mtu;
+
+ return (ip6_calcmtu(ifp, dst, mtu, mtup, alwaysfragp));
+}
+
+/*
+ * Calculate MTU based on transmit @ifp, route mtu @rt_mtu and
+ * hostcache data for @dst.
+ * Stores mtu and always-frag value into @mtup and @alwaysfragp.
+ *
+ * Returns 0 on success.
+ */
+static int
+ip6_calcmtu(struct ifnet *ifp, const struct in6_addr *dst, u_long rt_mtu,
+ u_long *mtup, int *alwaysfragp)
+{
+ u_long mtu = 0;
+ int alwaysfrag = 0;
+ int error = 0;
+
+ if (rt_mtu > 0) {
u_int32_t ifmtu;
struct in_conninfo inc;
@@ -1251,14 +1321,12 @@ ip6_getpmtu(struct route_in6 *ro_pmtu, struct route_in6 *ro,
inc.inc_flags |= INC_ISIPV6;
inc.inc6_faddr = *dst;
- if (ifp == NULL)
- ifp = ro_pmtu->ro_rt->rt_ifp;
ifmtu = IN6_LINKMTU(ifp);
mtu = tcp_hc_getmtu(&inc);
if (mtu)
- mtu = min(mtu, ro_pmtu->ro_rt->rt_mtu);
+ mtu = min(mtu, rt_mtu);
else
- mtu = ro_pmtu->ro_rt->rt_mtu;
+ mtu = rt_mtu;
if (mtu == 0)
mtu = ifmtu;
else if (mtu < IPV6_MMTU) {
@@ -1936,9 +2004,6 @@ do { \
{
u_long pmtu = 0;
struct ip6_mtuinfo mtuinfo;
- struct route_in6 sro;
-
- bzero(&sro, sizeof(sro));
if (!(so->so_state & SS_ISCONNECTED))
return (ENOTCONN);
@@ -1947,11 +2012,8 @@ do { \
* routing, or optional information to specify
* the outgoing interface.
*/
- error = ip6_getpmtu(&sro, NULL, NULL,
- &in6p->in6p_faddr, &pmtu, NULL,
- so->so_fibnum);
- if (sro.ro_rt)
- RTFREE(sro.ro_rt);
+ error = ip6_getpmtu_ctl(so->so_fibnum,
+ &in6p->in6p_faddr, &pmtu);
if (error)
break;
if (pmtu > IPV6_MAXPACKET)
OpenPOWER on IntegriCloud