diff --git a/sys/netinet/tcp_subr.c b/sys/netinet/tcp_subr.c index c3109c77eb0b..c21e1feb960f 100644 --- a/sys/netinet/tcp_subr.c +++ b/sys/netinet/tcp_subr.c @@ -1192,7 +1192,7 @@ tcp6_ctlinput(cmd, sa, d) in6_pcbnotify(&tcb, sa, th.th_dport, (struct sockaddr *)ip6cp->ip6c_src, - th.th_sport, cmd, notify); + th.th_sport, cmd, NULL, notify); inc.inc_fport = th.th_dport; inc.inc_lport = th.th_sport; @@ -1202,7 +1202,7 @@ tcp6_ctlinput(cmd, sa, d) syncache_unreach(&inc, &th); } else in6_pcbnotify(&tcb, sa, 0, (const struct sockaddr *)sa6_src, - 0, cmd, notify); + 0, cmd, NULL, notify); } #endif /* INET6 */ diff --git a/sys/netinet/tcp_timewait.c b/sys/netinet/tcp_timewait.c index c3109c77eb0b..c21e1feb960f 100644 --- a/sys/netinet/tcp_timewait.c +++ b/sys/netinet/tcp_timewait.c @@ -1192,7 +1192,7 @@ tcp6_ctlinput(cmd, sa, d) in6_pcbnotify(&tcb, sa, th.th_dport, (struct sockaddr *)ip6cp->ip6c_src, - th.th_sport, cmd, notify); + th.th_sport, cmd, NULL, notify); inc.inc_fport = th.th_dport; inc.inc_lport = th.th_sport; @@ -1202,7 +1202,7 @@ tcp6_ctlinput(cmd, sa, d) syncache_unreach(&inc, &th); } else in6_pcbnotify(&tcb, sa, 0, (const struct sockaddr *)sa6_src, - 0, cmd, notify); + 0, cmd, NULL, notify); } #endif /* INET6 */ diff --git a/sys/netinet6/in6_pcb.c b/sys/netinet6/in6_pcb.c index a19a1ebc835e..1c7278cf2399 100644 --- a/sys/netinet6/in6_pcb.c +++ b/sys/netinet6/in6_pcb.c @@ -610,12 +610,13 @@ in6_mapped_peeraddr(struct socket *so, struct sockaddr **nam) * Must be called at splnet. */ void -in6_pcbnotify(head, dst, fport_arg, src, lport_arg, cmd, notify) +in6_pcbnotify(head, dst, fport_arg, src, lport_arg, cmd, cmdarg, notify) struct inpcbhead *head; struct sockaddr *dst; const struct sockaddr *src; u_int fport_arg, lport_arg; int cmd; + void *cmdarg; struct inpcb *(*notify) __P((struct inpcb *, int)); { struct inpcb *inp, *ninp; @@ -661,6 +662,22 @@ in6_pcbnotify(head, dst, fport_arg, src, lport_arg, cmd, notify) if ((inp->inp_vflag & INP_IPV6) == 0) continue; + /* + * If the error designates a new path MTU for a destination + * and the application (associated with this socket) wanted to + * know the value, notify. Note that we notify for all + * disconnected sockets if the corresponding application + * wanted. This is because some UDP applications keep sending + * sockets disconnected. + * XXX: should we avoid to notify the value to TCP sockets? + */ + if (cmd == PRC_MSGSIZE && (inp->inp_flags & IN6P_MTU) != 0 && + (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr) || + IN6_ARE_ADDR_EQUAL(&inp->in6p_faddr, &sa6_dst->sin6_addr))) { + ip6_notify_pmtu(inp, (struct sockaddr_in6 *)dst, + (u_int32_t *)cmdarg); + } + /* * Detect if we should notify the error. If no source and * destination ports are specifed, but non-zero flowinfo and diff --git a/sys/netinet6/in6_pcb.h b/sys/netinet6/in6_pcb.h index cbe67c79081d..6cae30bef579 100644 --- a/sys/netinet6/in6_pcb.h +++ b/sys/netinet6/in6_pcb.h @@ -91,7 +91,7 @@ struct inpcb * struct in6_addr *, u_int, struct in6_addr *, u_int, int, struct ifnet *)); void in6_pcbnotify __P((struct inpcbhead *, struct sockaddr *, - u_int, const struct sockaddr *, u_int, int, + u_int, const struct sockaddr *, u_int, int, void *, struct inpcb *(*)(struct inpcb *, int))); struct inpcb * in6_rtchange __P((struct inpcb *, int)); diff --git a/sys/netinet6/ip6_input.c b/sys/netinet6/ip6_input.c index 07156064a14c..d42a444759a3 100644 --- a/sys/netinet6/ip6_input.c +++ b/sys/netinet6/ip6_input.c @@ -1307,6 +1307,45 @@ ip6_savecontrol(in6p, m, mp) #undef IS2292 } +void +ip6_notify_pmtu(in6p, dst, mtu) + struct inpcb *in6p; + struct sockaddr_in6 *dst; + u_int32_t *mtu; +{ + struct socket *so; + struct mbuf *m_mtu; + struct ip6_mtuinfo mtuctl; + + so = in6p->inp_socket; + + if (mtu == NULL) + return; + +#ifdef DIAGNOSTIC + if (so == NULL) /* I believe this is impossible */ + panic("ip6_notify_pmtu: socket is NULL"); +#endif + + bzero(&mtuctl, sizeof(mtuctl)); /* zero-clear for safety */ + mtuctl.ip6m_mtu = *mtu; + mtuctl.ip6m_addr = *dst; + in6_recoverscope(&mtuctl.ip6m_addr, &mtuctl.ip6m_addr.sin6_addr, NULL); + + if ((m_mtu = sbcreatecontrol((caddr_t)&mtuctl, sizeof(mtuctl), + IPV6_PATHMTU, IPPROTO_IPV6)) == NULL) + return; + + if (sbappendaddr(&so->so_rcv, (struct sockaddr *)dst, NULL, m_mtu) + == 0) { + m_freem(m_mtu); + /* XXX: should count statistics */ + } else + sorwakeup(so); + + return; +} + #ifdef PULLDOWN_TEST /* * pull single extension header from mbuf chain. returns single mbuf that diff --git a/sys/netinet6/raw_ip6.c b/sys/netinet6/raw_ip6.c index 56f37585e5db..00205ff01c65 100644 --- a/sys/netinet6/raw_ip6.c +++ b/sys/netinet6/raw_ip6.c @@ -278,6 +278,7 @@ rip6_ctlinput(cmd, sa, d) int off = 0; struct ip6ctlparam *ip6cp = NULL; const struct sockaddr_in6 *sa6_src = NULL; + void *cmdarg; struct inpcb *(*notify) __P((struct inpcb *, int)) = in6_rtchange; if (sa->sa_family != AF_INET6 || @@ -299,15 +300,17 @@ rip6_ctlinput(cmd, sa, d) m = ip6cp->ip6c_m; ip6 = ip6cp->ip6c_ip6; off = ip6cp->ip6c_off; + cmdarg = ip6cp->ip6c_cmdarg; sa6_src = ip6cp->ip6c_src; } else { m = NULL; ip6 = NULL; + cmdarg = NULL; sa6_src = &sa6_any; } (void) in6_pcbnotify(&ripcb, sa, 0, (const struct sockaddr *)sa6_src, - 0, cmd, notify); + 0, cmd, cmdarg, notify); } /* diff --git a/sys/netinet6/udp6_usrreq.c b/sys/netinet6/udp6_usrreq.c index deb2a3159817..d95fc2696c1b 100644 --- a/sys/netinet6/udp6_usrreq.c +++ b/sys/netinet6/udp6_usrreq.c @@ -435,6 +435,7 @@ udp6_ctlinput(cmd, sa, d) int off = 0; struct ip6ctlparam *ip6cp = NULL; const struct sockaddr_in6 *sa6_src = NULL; + void *cmdarg; struct inpcb *(*notify) __P((struct inpcb *, int)) = udp_notify; struct udp_portonly { u_int16_t uh_sport; @@ -460,10 +461,12 @@ udp6_ctlinput(cmd, sa, d) m = ip6cp->ip6c_m; ip6 = ip6cp->ip6c_ip6; off = ip6cp->ip6c_off; + cmdarg = ip6cp->ip6c_cmdarg; sa6_src = ip6cp->ip6c_src; } else { m = NULL; ip6 = NULL; + cmdarg = NULL; sa6_src = &sa6_any; } @@ -481,12 +484,12 @@ udp6_ctlinput(cmd, sa, d) m_copydata(m, off, sizeof(*uhp), (caddr_t)&uh); (void) in6_pcbnotify(&udb, sa, uh.uh_dport, - (struct sockaddr *)ip6cp->ip6c_src, - uh.uh_sport, cmd, notify); + (struct sockaddr *)ip6cp->ip6c_src, + uh.uh_sport, cmd, cmdarg, notify); } else (void) in6_pcbnotify(&udb, sa, 0, (const struct sockaddr *)sa6_src, - 0, cmd, notify); + 0, cmd, cmdarg, notify); } static int