summaryrefslogtreecommitdiffstats
path: root/sys/netinet6
diff options
context:
space:
mode:
authorume <ume@FreeBSD.org>2003-10-24 18:26:30 +0000
committerume <ume@FreeBSD.org>2003-10-24 18:26:30 +0000
commit881c4fa39150df7d0de2dae7ae808f6a73cb199a (patch)
tree6ce05cb7459c9a9be90d670c12bfddbbbcb6946d /sys/netinet6
parent0b2009d038122fd790a91ca95c5d9044ff2715c0 (diff)
downloadFreeBSD-src-881c4fa39150df7d0de2dae7ae808f6a73cb199a.zip
FreeBSD-src-881c4fa39150df7d0de2dae7ae808f6a73cb199a.tar.gz
Switch Advanced Sockets API for IPv6 from RFC2292 to RFC3542
(aka RFC2292bis). Though I believe this commit doesn't break backward compatibility againt existing binaries, it breaks backward compatibility of API. Now, the applications which use Advanced Sockets API such as telnet, ping6, mld6query and traceroute6 use RFC3542 API. Obtained from: KAME
Diffstat (limited to 'sys/netinet6')
-rw-r--r--sys/netinet6/icmp6.c41
-rw-r--r--sys/netinet6/in6.h123
-rw-r--r--sys/netinet6/in6_pcb.c3
-rw-r--r--sys/netinet6/in6_var.h5
-rw-r--r--sys/netinet6/ip6_input.c89
-rw-r--r--sys/netinet6/ip6_output.c1235
-rw-r--r--sys/netinet6/ip6_var.h41
-rw-r--r--sys/netinet6/mld6.c2
-rw-r--r--sys/netinet6/nd6.c65
-rw-r--r--sys/netinet6/nd6.h37
-rw-r--r--sys/netinet6/nd6_rtr.c3
-rw-r--r--sys/netinet6/raw_ip6.c48
-rw-r--r--sys/netinet6/route6.c3
-rw-r--r--sys/netinet6/udp6_output.c5
14 files changed, 1273 insertions, 427 deletions
diff --git a/sys/netinet6/icmp6.c b/sys/netinet6/icmp6.c
index aa96326..30e534f 100644
--- a/sys/netinet6/icmp6.c
+++ b/sys/netinet6/icmp6.c
@@ -245,9 +245,15 @@ icmp6_error(m, type, code, param)
oip6 = mtod(m, struct ip6_hdr *);
/*
- * Multicast destination check. For unrecognized option errors,
- * this check has already done in ip6_unknown_opt(), so we can
- * check only for other errors.
+ * If the destination address of the erroneous packet is a multicast
+ * address, or the packet was sent using link-layer multicast,
+ * we should basically suppress sending an error (RFC 2463, Section
+ * 2.4).
+ * We have two exceptions (the item e.2 in that section):
+ * - the Pakcet Too Big message can be sent for path MTU discovery.
+ * - the Parameter Problem Message that can be allowed an icmp6 error
+ * in the option type field. This check has been done in
+ * ip6_unknown_opt(), so we can just check the type and code.
*/
if ((m->m_flags & (M_BCAST|M_MCAST) ||
IN6_IS_ADDR_MULTICAST(&oip6->ip6_dst)) &&
@@ -256,7 +262,10 @@ icmp6_error(m, type, code, param)
code != ICMP6_PARAMPROB_OPTION)))
goto freeit;
- /* Source address check. XXX: the case of anycast source? */
+ /*
+ * RFC 2463, 2.4 (e.5): source address check.
+ * XXX: the case of anycast source?
+ */
if (IN6_IS_ADDR_UNSPECIFIED(&oip6->ip6_src) ||
IN6_IS_ADDR_MULTICAST(&oip6->ip6_src))
goto freeit;
@@ -1100,6 +1109,26 @@ icmp6_mtudisc_update(ip6cp, validated)
struct rtentry *rt = NULL;
struct sockaddr_in6 sin6;
+#if 0
+ /*
+ * RFC2460 section 5, last paragraph.
+ * even though minimum link MTU for IPv6 is IPV6_MMTU,
+ * we may see ICMPv6 too big with mtu < IPV6_MMTU
+ * due to packet translator in the middle.
+ * see ip6_output() and ip6_getpmtu() "alwaysfrag" case for
+ * special handling.
+ */
+ if (mtu < IPV6_MMTU)
+ return;
+#endif
+
+ /*
+ * we reject ICMPv6 too big with abnormally small value.
+ * XXX what is the good definition of "abnormally small"?
+ */
+ if (mtu < sizeof(struct ip6_hdr) + sizeof(struct ip6_frag) + 8)
+ return;
+
if (!validated)
return;
@@ -2122,7 +2151,9 @@ icmp6_reflect(m, off)
ip6->ip6_vfc &= ~IPV6_VERSION_MASK;
ip6->ip6_vfc |= IPV6_VERSION;
ip6->ip6_nxt = IPPROTO_ICMPV6;
- if (m->m_pkthdr.rcvif) {
+ if (outif)
+ ip6->ip6_hlim = ND_IFINFO(outif)->chlim;
+ else if (m->m_pkthdr.rcvif) {
/* XXX: This may not be the outgoing interface */
ip6->ip6_hlim = ND_IFINFO(m->m_pkthdr.rcvif)->chlim;
} else
diff --git a/sys/netinet6/in6.h b/sys/netinet6/in6.h
index 92127f3..45481e9 100644
--- a/sys/netinet6/in6.h
+++ b/sys/netinet6/in6.h
@@ -407,15 +407,6 @@ struct route_in6 {
* Options for use with [gs]etsockopt at the IPV6 level.
* First word of comment is data type; bool is stored in int.
*/
-#define IPV6_JOIN_GROUP 12 /* ip6_mreq; join a group membership */
-#define IPV6_LEAVE_GROUP 13 /* ip6_mreq; leave a group membership */
-#define IPV6_MULTICAST_HOPS 10 /* int; set/get IP6 multicast hops */
-#define IPV6_MULTICAST_IF 9 /* u_int; set/get IP6 multicast i/f */
-#define IPV6_MULTICAST_LOOP 11 /* u_int; set/get IP6 multicast loopback */
-#define IPV6_UNICAST_HOPS 4 /* int; IP6 hops */
-#define IPV6_V6ONLY 27 /* bool; only bind INET6 at wildcard bind */
-
-#if __BSD_VISIBLE
/* no hdrincl */
#if 0 /* the followings are relic in IPv4 and hence are disabled */
#define IPV6_OPTIONS 1 /* buf/ip6_opts; set/get IP6 options */
@@ -425,18 +416,27 @@ struct route_in6 {
#define IPV6_RETOPTS 8 /* ip6_opts; set/get IP6 options */
#endif
#define IPV6_SOCKOPT_RESERVED1 3 /* reserved for future use */
+#define IPV6_UNICAST_HOPS 4 /* int; IP6 hops */
+#define IPV6_MULTICAST_IF 9 /* u_int; set/get IP6 multicast i/f */
+#define IPV6_MULTICAST_HOPS 10 /* int; set/get IP6 multicast hops */
+#define IPV6_MULTICAST_LOOP 11 /* u_int; set/get IP6 multicast loopback */
+#define IPV6_JOIN_GROUP 12 /* ip6_mreq; join a group membership */
+#define IPV6_LEAVE_GROUP 13 /* ip6_mreq; leave a group membership */
#define IPV6_PORTRANGE 14 /* int; range to choose for unspec port */
#define ICMP6_FILTER 18 /* icmp6_filter; icmp6 filter */
/* RFC2292 options */
-#define IPV6_PKTINFO 19 /* bool; send/recv if, src/dst addr */
-#define IPV6_HOPLIMIT 20 /* bool; hop limit */
-#define IPV6_NEXTHOP 21 /* bool; next hop addr */
-#define IPV6_HOPOPTS 22 /* bool; hop-by-hop option */
-#define IPV6_DSTOPTS 23 /* bool; destination option */
-#define IPV6_RTHDR 24 /* bool; routing header */
-#define IPV6_PKTOPTIONS 25 /* buf/cmsghdr; set/get IPv6 options */
+#ifdef _KERNEL
+#define IPV6_2292PKTINFO 19 /* bool; send/recv if, src/dst addr */
+#define IPV6_2292HOPLIMIT 20 /* bool; hop limit */
+#define IPV6_2292NEXTHOP 21 /* bool; next hop addr */
+#define IPV6_2292HOPOPTS 22 /* bool; hop-by-hop option */
+#define IPV6_2292DSTOPTS 23 /* bool; destinaion option */
+#define IPV6_2292RTHDR 24 /* bool; routing header */
+#define IPV6_2292PKTOPTIONS 25 /* buf/cmsghdr; set/get IPv6 options */
+#endif
#define IPV6_CHECKSUM 26 /* int; checksum offset for raw socket */
+#define IPV6_V6ONLY 27 /* bool; make AF_INET6 sockets v6 only */
#ifndef _KERNEL
#define IPV6_BINDV6ONLY IPV6_V6ONLY
#endif
@@ -454,6 +454,51 @@ struct route_in6 {
#define IPV6_FW_GET 34 /* get entire firewall rule chain */
#endif
+/* new socket options introduced in RFC2292bis */
+#define IPV6_RTHDRDSTOPTS 35 /* ip6_dest; send dst option before rthdr */
+
+#define IPV6_RECVPKTINFO 36 /* bool; recv if, dst addr */
+#define IPV6_RECVHOPLIMIT 37 /* bool; recv hop limit */
+#define IPV6_RECVRTHDR 38 /* bool; recv routing header */
+#define IPV6_RECVHOPOPTS 39 /* bool; recv hop-by-hop option */
+#define IPV6_RECVDSTOPTS 40 /* bool; recv dst option after rthdr */
+#ifdef _KERNEL
+#define IPV6_RECVRTHDRDSTOPTS 41 /* bool; recv dst option before rthdr */
+#endif
+
+#define IPV6_USE_MIN_MTU 42 /* bool; send packets at the minimum MTU */
+#define IPV6_RECVPATHMTU 43 /* bool; notify an according MTU */
+
+#define IPV6_PATHMTU 44 /* mtuinfo; get the current path MTU (sopt),
+ 4 bytes int; MTU notification (cmsg) */
+#if 0 /*obsoleted during 2292bis -> 3542*/
+#define IPV6_REACHCONF 45 /* no data; ND reachability confirm
+ (cmsg only/not in of RFC3542) */
+#endif
+
+/* more new socket options introduced in RFC2292bis */
+#define IPV6_PKTINFO 46 /* in6_pktinfo; send if, src addr */
+#define IPV6_HOPLIMIT 47 /* int; send hop limit */
+#define IPV6_NEXTHOP 48 /* sockaddr; next hop addr */
+#define IPV6_HOPOPTS 49 /* ip6_hbh; send hop-by-hop option */
+#define IPV6_DSTOPTS 50 /* ip6_dest; send dst option befor rthdr */
+#define IPV6_RTHDR 51 /* ip6_rthdr; send routing header */
+#if 0
+#define IPV6_PKTOPTIONS 52 /* buf/cmsghdr; set/get IPv6 options */
+ /* obsoleted by 2292bis */
+#endif
+
+#define IPV6_RECVTCLASS 57 /* bool; recv traffic class values */
+
+#define IPV6_AUTOFLOWLABEL 59 /* bool; attach flowlabel automagically */
+
+#define IPV6_TCLASS 61 /* int; send traffic class value */
+#define IPV6_DONTFRAG 62 /* bool; disable IPv6 fragmentation */
+
+#define IPV6_PREFER_TEMPADDR 63 /* int; prefer temporary addresses as
+ * the source address.
+ */
+
/* to define items, should talk with KAME guys first, for *BSD compatibility */
#define IPV6_RTHDR_LOOSE 0 /* this hop need not be a neighbor. XXX old spec */
@@ -483,6 +528,14 @@ struct in6_pktinfo {
};
/*
+ * Control structure for IPV6_RECVPATHMTU socket option.
+ */
+struct ip6_mtuinfo {
+ struct sockaddr_in6 ip6m_addr; /* or sockaddr_storage? */
+ u_int32_t ip6m_mtu;
+};
+
+/*
* Argument for IPV6_PORTRANGE:
* - which range to search when port is unspecified at bind() or connect()
*/
@@ -490,6 +543,7 @@ struct in6_pktinfo {
#define IPV6_PORTRANGE_HIGH 1 /* "high" - request firewall bypass */
#define IPV6_PORTRANGE_LOW 2 /* "low" - vouchsafe security */
+#if __BSD_VISIBLE
/*
* Definitions for inet6 sysctl operations.
*
@@ -544,6 +598,7 @@ struct in6_pktinfo {
/* New entries should be added here from current IPV6CTL_MAXID value. */
/* to define items, should talk with KAME guys first, for *BSD compatibility */
#define IPV6CTL_MAXID 42
+#endif /* __BSD_VISIBLE */
/*
* Redefinition of mbuf flags
@@ -584,6 +639,8 @@ typedef __size_t size_t;
#define _SIZE_T_DECLARED
#endif
+#if __BSD_VISIBLE
+
__BEGIN_DECLS
struct cmsghdr;
@@ -591,14 +648,14 @@ extern int inet6_option_space __P((int));
extern int inet6_option_init __P((void *, struct cmsghdr **, int));
extern int inet6_option_append __P((struct cmsghdr *, const uint8_t *,
int, int));
-extern uint8_t *inet6_option_alloc __P((struct cmsghdr *, int, int, int));
-extern int inet6_option_next __P((const struct cmsghdr *, uint8_t **));
-extern int inet6_option_find __P((const struct cmsghdr *, uint8_t **, int));
+extern u_int8_t *inet6_option_alloc __P((struct cmsghdr *, int, int, int));
+extern int inet6_option_next __P((const struct cmsghdr *, u_int8_t **));
+extern int inet6_option_find __P((const struct cmsghdr *, u_int8_t **, int));
extern size_t inet6_rthdr_space __P((int, int));
extern struct cmsghdr *inet6_rthdr_init __P((void *, int));
extern int inet6_rthdr_add __P((struct cmsghdr *, const struct in6_addr *,
- unsigned int));
+ unsigned int));
extern int inet6_rthdr_lasthop __P((struct cmsghdr *, unsigned int));
#if 0 /* not implemented yet */
extern int inet6_rthdr_reverse __P((const struct cmsghdr *, struct cmsghdr *));
@@ -607,19 +664,19 @@ extern int inet6_rthdr_segments __P((const struct cmsghdr *));
extern struct in6_addr *inet6_rthdr_getaddr __P((struct cmsghdr *, int));
extern int inet6_rthdr_getflags __P((const struct cmsghdr *, int));
-extern int inet6_opt_init __P((void *, size_t));
-extern int inet6_opt_append __P((void *, size_t, int, uint8_t,
- size_t, uint8_t, void **));
-extern int inet6_opt_finish __P((void *, size_t, int));
-extern int inet6_opt_set_val __P((void *, size_t, void *, int));
-
-extern int inet6_opt_next __P((void *, size_t, int, uint8_t *,
- size_t *, void **));
-extern int inet6_opt_find __P((void *, size_t, int, uint8_t,
- size_t *, void **));
-extern int inet6_opt_get_val __P((void *, size_t, void *, int));
-extern size_t inet6_rth_space __P((int, int));
-extern void *inet6_rth_init __P((void *, int, int, int));
+extern int inet6_opt_init __P((void *, socklen_t));
+extern int inet6_opt_append __P((void *, socklen_t, int, u_int8_t, socklen_t,
+ u_int8_t, void **));
+extern int inet6_opt_finish __P((void *, socklen_t, int));
+extern int inet6_opt_set_val __P((void *, int, void *, socklen_t));
+
+extern int inet6_opt_next __P((void *, socklen_t, int, u_int8_t *, socklen_t *,
+ void **));
+extern int inet6_opt_find __P((void *, socklen_t, int, u_int8_t, socklen_t *,
+ void **));
+extern int inet6_opt_get_val __P((void *, int, void *, socklen_t));
+extern socklen_t inet6_rth_space __P((int, int));
+extern void *inet6_rth_init __P((void *, socklen_t, int, int));
extern int inet6_rth_add __P((void *, const struct in6_addr *));
extern int inet6_rth_reverse __P((const void *, void *));
extern int inet6_rth_segments __P((const void *));
diff --git a/sys/netinet6/in6_pcb.c b/sys/netinet6/in6_pcb.c
index a49bb59..eb62328 100644
--- a/sys/netinet6/in6_pcb.c
+++ b/sys/netinet6/in6_pcb.c
@@ -444,8 +444,7 @@ in6_pcbdetach(inp)
so->so_pcb = NULL;
sotryfree(so);
}
- if (inp->in6p_options)
- m_freem(inp->in6p_options);
+
ip6_freepcbopts(inp->in6p_outputopts);
ip6_freemoptions(inp->in6p_moptions);
if (inp->in6p_route.ro_rt)
diff --git a/sys/netinet6/in6_var.h b/sys/netinet6/in6_var.h
index 5a520fa..b481b04 100644
--- a/sys/netinet6/in6_var.h
+++ b/sys/netinet6/in6_var.h
@@ -391,7 +391,10 @@ struct in6_rrenumreq {
#define SIOCGIFAFLAG_IN6 _IOWR('i', 73, struct in6_ifreq)
#define SIOCGDRLST_IN6 _IOWR('i', 74, struct in6_drlist)
-#define SIOCGPRLST_IN6 _IOWR('i', 75, struct in6_prlist)
+#ifdef _KERNEL
+/* XXX: SIOCGPRLST_IN6 is exposed in KAME but in6_oprlist is not. */
+#define SIOCGPRLST_IN6 _IOWR('i', 75, struct in6_oprlist)
+#endif
#ifdef _KERNEL
#define OSIOCGIFINFO_IN6 _IOWR('i', 76, struct in6_ondireq)
#endif
diff --git a/sys/netinet6/ip6_input.c b/sys/netinet6/ip6_input.c
index 54f252d..b823097 100644
--- a/sys/netinet6/ip6_input.c
+++ b/sys/netinet6/ip6_input.c
@@ -916,7 +916,7 @@ ip6_process_hopopts(m, opthead, hbhlen, rtalertp, plenp)
}
optlen = *(opt + 1) + 2;
break;
- case IP6OPT_RTALERT:
+ case IP6OPT_ROUTER_ALERT:
/* XXX may need check for alignment */
if (hbhlen < IP6OPT_RTALERT_LEN) {
ip6stat.ip6s_toosmall++;
@@ -1077,14 +1077,9 @@ ip6_savecontrol(in6p, mp, ip6, m)
struct ip6_hdr *ip6;
struct mbuf *m;
{
-#if __FreeBSD_version >= 500000
+#define IS2292(x, y) ((in6p->in6p_flags & IN6P_RFC2292) ? (x) : (y))
struct thread *td = curthread; /* XXX */
-#else
- struct proc *td = curproc; /* XXX */
-#endif
int privileged = 0;
- int rthdr_exist = 0;
-
if (td && !suser(td))
privileged++;
@@ -1096,9 +1091,8 @@ ip6_savecontrol(in6p, mp, ip6, m)
microtime(&tv);
*mp = sbcreatecontrol((caddr_t) &tv, sizeof(tv),
SCM_TIMESTAMP, SOL_SOCKET);
- if (*mp) {
+ if (*mp)
mp = &(*mp)->m_next;
- }
}
#endif
@@ -1113,20 +1107,32 @@ ip6_savecontrol(in6p, mp, ip6, m)
*mp = sbcreatecontrol((caddr_t) &pi6,
sizeof(struct in6_pktinfo),
- IPV6_PKTINFO, IPPROTO_IPV6);
- if (*mp) {
+ IS2292(IPV6_2292PKTINFO, IPV6_PKTINFO), IPPROTO_IPV6);
+ if (*mp)
mp = &(*mp)->m_next;
- }
}
if ((in6p->in6p_flags & IN6P_HOPLIMIT) != 0) {
int hlim = ip6->ip6_hlim & 0xff;
*mp = sbcreatecontrol((caddr_t) &hlim, sizeof(int),
- IPV6_HOPLIMIT, IPPROTO_IPV6);
- if (*mp) {
+ IS2292(IPV6_2292HOPLIMIT, IPV6_HOPLIMIT), IPPROTO_IPV6);
+ if (*mp)
+ mp = &(*mp)->m_next;
+ }
+
+ if ((in6p->in6p_flags & IN6P_TCLASS) != 0) {
+ u_int32_t flowinfo;
+ int tclass;
+
+ flowinfo = (u_int32_t)ntohl(ip6->ip6_flow & IPV6_FLOWINFO_MASK);
+ flowinfo >>= 20;
+
+ tclass = flowinfo & 0xff;
+ *mp = sbcreatecontrol((caddr_t) &tclass, sizeof(tclass),
+ IPV6_TCLASS, IPPROTO_IPV6);
+ if (*mp)
mp = &(*mp)->m_next;
- }
}
/*
@@ -1135,7 +1141,11 @@ ip6_savecontrol(in6p, mp, ip6, m)
* be some hop-by-hop options which can be returned to normal user.
* See RFC 2292 section 6.
*/
- if ((in6p->in6p_flags & IN6P_HOPOPTS) != 0 && privileged) {
+ if ((in6p->in6p_flags & IN6P_HOPOPTS) != 0) {
+#ifdef DIAGNOSTIC
+ if (!privileged)
+ panic("IN6P_HOPOPTS is set for unprivileged socket");
+#endif
/*
* Check if a hop-by-hop options header is contatined in the
* received packet, and if so, store the options as ancillary
@@ -1143,7 +1153,6 @@ ip6_savecontrol(in6p, mp, ip6, m)
* just after the IPv6 header, which is assured through the
* IPv6 input processing.
*/
- struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
if (ip6->ip6_nxt == IPPROTO_HOPOPTS) {
struct ip6_hbh *hbh;
int hbhlen = 0;
@@ -1178,50 +1187,17 @@ ip6_savecontrol(in6p, mp, ip6, m)
* Note: this constraint is removed in 2292bis.
*/
*mp = sbcreatecontrol((caddr_t)hbh, hbhlen,
- IPV6_HOPOPTS, IPPROTO_IPV6);
- if (*mp) {
+ IS2292(IPV6_2292HOPOPTS, IPV6_HOPOPTS),
+ IPPROTO_IPV6);
+ if (*mp)
mp = &(*mp)->m_next;
- }
#ifdef PULLDOWN_TEST
m_freem(ext);
#endif
}
}
- /* IPV6_DSTOPTS and IPV6_RTHDR socket options */
- if ((in6p->in6p_flags & (IN6P_DSTOPTS | IN6P_RTHDRDSTOPTS)) != 0) {
- int proto, off, nxt;
-
- /*
- * go through the header chain to see if a routing header is
- * contained in the packet. We need this information to store
- * destination options headers (if any) properly.
- * XXX: performance issue. We should record this info when
- * processing extension headers in incoming routine.
- * (todo) use m_aux?
- */
- proto = IPPROTO_IPV6;
- off = 0;
- nxt = -1;
- while (1) {
- int newoff;
-
- newoff = ip6_nexthdr(m, off, proto, &nxt);
- if (newoff < 0)
- break;
- if (newoff < off) /* invalid, check for safety */
- break;
- if ((proto = nxt) == IPPROTO_ROUTING) {
- rthdr_exist = 1;
- break;
- }
- off = newoff;
- }
- }
-
- if ((in6p->in6p_flags &
- (IN6P_RTHDR | IN6P_DSTOPTS | IN6P_RTHDRDSTOPTS)) != 0) {
- struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
+ if ((in6p->in6p_flags & (IN6P_RTHDR | IN6P_DSTOPTS)) != 0) {
int nxt = ip6->ip6_nxt, off = sizeof(struct ip6_hdr);
/*
@@ -1293,7 +1269,7 @@ ip6_savecontrol(in6p, mp, ip6, m)
break;
*mp = sbcreatecontrol((caddr_t)ip6e, elen,
- IPV6_DSTOPTS,
+ IS2292(IPV6_2292DSTOPTS, IPV6_DSTOPTS),
IPPROTO_IPV6);
if (*mp)
mp = &(*mp)->m_next;
@@ -1303,7 +1279,7 @@ ip6_savecontrol(in6p, mp, ip6, m)
break;
*mp = sbcreatecontrol((caddr_t)ip6e, elen,
- IPV6_RTHDR,
+ IS2292(IPV6_2292RTHDR, IPV6_RTHDR),
IPPROTO_IPV6);
if (*mp)
mp = &(*mp)->m_next;
@@ -1339,6 +1315,7 @@ ip6_savecontrol(in6p, mp, ip6, m)
;
}
+#undef IS2292
}
#ifdef PULLDOWN_TEST
diff --git a/sys/netinet6/ip6_output.c b/sys/netinet6/ip6_output.c
index 51f5b7a..b4859ec 100644
--- a/sys/netinet6/ip6_output.c
+++ b/sys/netinet6/ip6_output.c
@@ -128,8 +128,14 @@ struct ip6_exthdrs {
struct mbuf *ip6e_dest2;
};
+static int ip6_pcbopt __P((int, u_char *, int, struct ip6_pktopts **,
+ int, int));
static int ip6_pcbopts __P((struct ip6_pktopts **, struct mbuf *,
struct socket *, struct sockopt *));
+static int ip6_getpcbopt __P((struct ip6_pktopts *, int, struct sockopt *));
+static int ip6_setpktoption __P((int, u_char *, int, struct ip6_pktopts *, int,
+ int, int, int));
+
static int ip6_setmoptions __P((int, struct ip6_moptions **, struct mbuf *));
static int ip6_getmoptions __P((int, struct ip6_moptions *, struct mbuf **));
static int ip6_copyexthdr __P((struct mbuf **, caddr_t, int));
@@ -138,7 +144,7 @@ static int ip6_insertfraghdr __P((struct mbuf *, struct mbuf *, int,
static int ip6_insert_jumboopt __P((struct ip6_exthdrs *, u_int32_t));
static int ip6_splithdr __P((struct mbuf *, struct ip6_exthdrs *));
static int ip6_getpmtu __P((struct route_in6 *, struct route_in6 *,
- struct ifnet *, struct in6_addr *, u_long *));
+ struct ifnet *, struct in6_addr *, u_long *, int *));
/*
@@ -171,6 +177,7 @@ ip6_output(m0, opt, ro, flags, im6o, ifpp, inp)
int error = 0;
struct in6_ifaddr *ia = NULL;
u_long mtu;
+ int alwaysfrag, dontfrag;
u_int32_t optlen = 0, plen = 0, unfragpartlen = 0;
struct ip6_exthdrs exthdrs;
struct in6_addr finaldst;
@@ -533,6 +540,33 @@ skip_ipsec2:;
dst->sin6_len = sizeof(struct sockaddr_in6);
dst->sin6_addr = ip6->ip6_dst;
}
+
+ /*
+ * if specified, try to fill in the traffic class field.
+ * do not override if a non-zero value is already set.
+ * we check the diffserv field and the ecn field separately.
+ */
+ if (opt && opt->ip6po_tclass >= 0) {
+ int mask = 0;
+
+ if ((ip6->ip6_flow & htonl(0xfc << 20)) == 0)
+ mask |= 0xfc;
+ if ((ip6->ip6_flow & htonl(0x03 << 20)) == 0)
+ mask |= 0x03;
+ if (mask != 0)
+ ip6->ip6_flow |= htonl((opt->ip6po_tclass & mask) << 20);
+ }
+
+ /* fill in or override the hop limit field, if necessary. */
+ if (opt && opt->ip6po_hlim != -1)
+ ip6->ip6_hlim = opt->ip6po_hlim & 0xff;
+ else if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
+ if (im6o != NULL)
+ ip6->ip6_hlim = im6o->im6o_multicast_hlim;
+ else
+ ip6->ip6_hlim = ip6_defmcasthlim;
+ }
+
#if defined(IPSEC) || defined(FAST_IPSEC)
if (needipsec && needipsectun) {
struct ipsec_output_state state;
@@ -760,7 +794,8 @@ skip_ipsec2:;
* loop back a copy if this host actually belongs to the
* destination group on the loopback interface.
*/
- if (ip6->ip6_hlim == 0 || (ifp->if_flags & IFF_LOOPBACK)) {
+ if (ip6->ip6_hlim == 0 || (ifp->if_flags & IFF_LOOPBACK) ||
+ IN6_IS_ADDR_MC_INTFACELOCAL(&ip6->ip6_dst)) {
m_freem(m);
goto done;
}
@@ -774,7 +809,8 @@ skip_ipsec2:;
*ifpp = ifp;
/* Determine path MTU. */
- if ((error = ip6_getpmtu(ro_pmtu, ro, ifp, &finaldst, &mtu)) != 0)
+ if ((error = ip6_getpmtu(ro_pmtu, ro, ifp, &finaldst, &mtu,
+ &alwaysfrag)) != 0)
goto bad;
/*
@@ -891,40 +927,81 @@ skip_ipsec2:;
/*
* Send the packet to the outgoing interface.
* If necessary, do IPv6 fragmentation before sending.
+ *
+ * the logic here is rather complex:
+ * 1: normal case (dontfrag == 0, alwaysfrag == 0)
+ * 1-a: send as is if tlen <= path mtu
+ * 1-b: fragment if tlen > path mtu
+ *
+ * 2: if user asks us not to fragment (dontfrag == 1)
+ * 2-a: send as is if tlen <= interface mtu
+ * 2-b: error if tlen > interface mtu
+ *
+ * 3: if we always need to attach fragment header (alwaysfrag == 1)
+ * always fragment
+ *
+ * 4: if dontfrag == 1 && alwaysfrag == 1
+ * error, as we cannot handle this conflicting request
*/
tlen = m->m_pkthdr.len;
- if (tlen <= mtu
-#ifdef notyet
- /*
- * On any link that cannot convey a 1280-octet packet in one piece,
- * link-specific fragmentation and reassembly must be provided at
- * a layer below IPv6. [RFC 2460, sec.5]
- * Thus if the interface has ability of link-level fragmentation,
- * we can just send the packet even if the packet size is
- * larger than the link's MTU.
- * XXX: IFF_FRAGMENTABLE (or such) flag has not been defined yet...
- */
-
- || ifp->if_flags & IFF_FRAGMENTABLE
-#endif
- )
- {
- /* Record statistics for this interface address. */
- if (ia && !(flags & IPV6_FORWARDING)) {
- ia->ia_ifa.if_opackets++;
- ia->ia_ifa.if_obytes += m->m_pkthdr.len;
- }
+
+ if (opt && (opt->ip6po_flags & IP6PO_DONTFRAG))
+ dontfrag = 1;
+ else
+ dontfrag = 0;
+ if (dontfrag && alwaysfrag) { /* case 4 */
+ /* conflicting request - can't transmit */
+ error = EMSGSIZE;
+ goto bad;
+ }
+ if (dontfrag && tlen > IN6_LINKMTU(ifp)) { /* case 2-b */
+ /*
+ * Even if the DONTFRAG option is specified, we cannot send the
+ * packet when the data length is larger than the MTU of the
+ * outgoing interface.
+ * Notify the error by sending IPV6_PATHMTU ancillary data as
+ * well as returning an error code (the latter is not described
+ * in the API spec.)
+ */
+ u_int32_t mtu32;
+ struct ip6ctlparam ip6cp;
+
+ mtu32 = (u_int32_t)mtu;
+ bzero(&ip6cp, sizeof(ip6cp));
+ ip6cp.ip6c_cmdarg = (void *)&mtu32;
+ pfctlinput2(PRC_MSGSIZE, (struct sockaddr *)&ro_pmtu->ro_dst,
+ (void *)&ip6cp);
+
+ error = EMSGSIZE;
+ goto bad;
+ }
+
+ /*
+ * transmit packet without fragmentation
+ */
+ if (dontfrag || (!alwaysfrag && tlen <= mtu)) { /* case 1-a and 2-a */
+ struct in6_ifaddr *ia6;
+
+ ip6 = mtod(m, struct ip6_hdr *);
+ ia6 = in6_ifawithifp(ifp, &ip6->ip6_src);
+ if (ia6) {
+ /* Record statistics for this interface address. */
+ ia6->ia_ifa.if_opackets++;
+ ia6->ia_ifa.if_obytes += m->m_pkthdr.len;
+ }
#ifdef IPSEC
/* clean ipsec history once it goes out of the node */
ipsec_delaux(m);
#endif
error = nd6_output(ifp, origifp, m, dst, ro->ro_rt);
goto done;
- } else if (mtu < IPV6_MMTU) {
- /*
- * note that path MTU is never less than IPV6_MMTU
- * (see icmp6_input).
- */
+ }
+
+ /*
+ * try to fragment the packet. case 1-b and 3
+ */
+ if (mtu < IPV6_MMTU) {
+ /* path MTU cannot be less than IPV6_MMTU */
error = EMSGSIZE;
in6_ifstat_inc(ifp, ifs6_out_fragfail);
goto bad;
@@ -1261,22 +1338,21 @@ ip6_insertfraghdr(m0, m, hlen, frghdrp)
}
static int
-ip6_getpmtu(ro_pmtu, ro, ifp, dst, mtup)
+ip6_getpmtu(ro_pmtu, ro, ifp, dst, mtup, alwaysfragp)
struct route_in6 *ro_pmtu, *ro;
struct ifnet *ifp;
struct in6_addr *dst;
u_long *mtup;
+ int *alwaysfragp;
{
u_int32_t mtu = 0;
+ int alwaysfrag = 0;
int error = 0;
- /*
- * Determine path 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;
+ (struct sockaddr_in6 *)&ro_pmtu->ro_dst;
if (ro_pmtu->ro_rt &&
((ro->ro_rt->rt_flags & RTF_UP) == 0 ||
!IN6_ARE_ADDR_EQUAL(&sa6_dst->sin6_addr, dst))) {
@@ -1301,7 +1377,18 @@ ip6_getpmtu(ro_pmtu, ro, ifp, dst, mtup)
mtu = ro_pmtu->ro_rt->rt_rmx.rmx_mtu;
if (mtu == 0)
mtu = ifmtu;
- else if (mtu > ifmtu || mtu == 0) {
+ else if (mtu < IPV6_MMTU) {
+ /*
+ * RFC2460 section 5, last paragraph:
+ * if we record ICMPv6 too big message with
+ * mtu < IPV6_MMTU, transmit packets sized IPV6_MMTU
+ * or smaller, with framgent header attached.
+ * (fragment header is needed regardless from the
+ * packet size, for translators to identify packets)
+ */
+ alwaysfrag = 1;
+ mtu = IPV6_MMTU;
+ } else if (mtu > ifmtu) {
/*
* The MTU on the route is larger than the MTU on
* the interface! This shouldn't happen, unless the
@@ -1320,6 +1407,8 @@ ip6_getpmtu(ro_pmtu, ro, ifp, dst, mtup)
error = EHOSTUNREACH; /* XXX */
*mtup = mtu;
+ if (alwaysfragp)
+ *alwaysfragp = alwaysfrag;
return (error);
}
@@ -1331,7 +1420,8 @@ ip6_ctloutput(so, sopt)
struct socket *so;
struct sockopt *sopt;
{
- int privileged;
+ int privileged, optdatalen, uproto;
+ void *optdata;
struct inpcb *in6p = sotoinpcb(so);
int error, optval;
int level, op, optname;
@@ -1350,13 +1440,17 @@ ip6_ctloutput(so, sopt)
error = optval = 0;
privileged = (td == 0 || suser(td)) ? 0 : 1;
+ uproto = (int)so->so_proto->pr_protocol;
if (level == IPPROTO_IPV6) {
switch (op) {
case SOPT_SET:
switch (optname) {
+ case IPV6_2292PKTOPTIONS:
+#ifdef IPV6_PKTOPTIONS
case IPV6_PKTOPTIONS:
+#endif
{
struct mbuf *m;
@@ -1385,11 +1479,25 @@ ip6_ctloutput(so, sopt)
* receiving ANY hbh/dst options in order to avoid
* overhead of parsing options in the kernel.
*/
+ case IPV6_RECVHOPOPTS:
+ case IPV6_RECVDSTOPTS:
+ case IPV6_RECVRTHDRDSTOPTS:
+ if (!privileged) {
+ error = EPERM;
+ break;
+ }
+ /* FALLTHROUGH */
case IPV6_UNICAST_HOPS:
- case IPV6_CHECKSUM:
+ case IPV6_HOPLIMIT:
case IPV6_FAITH:
+ case IPV6_RECVPKTINFO:
+ case IPV6_RECVHOPLIMIT:
+ case IPV6_RECVRTHDR:
+ case IPV6_RECVPATHMTU:
+ case IPV6_RECVTCLASS:
case IPV6_V6ONLY:
+ case IPV6_AUTOFLOWLABEL:
if (optlen != sizeof(int)) {
error = EINVAL;
break;
@@ -1418,16 +1526,103 @@ do { \
else \
in6p->in6p_flags &= ~(bit); \
} while (/*CONSTCOND*/ 0)
+#define OPTSET2292(bit) \
+do { \
+ in6p->in6p_flags |= IN6P_RFC2292; \
+ if (optval) \
+ in6p->in6p_flags |= (bit); \
+ else \
+ in6p->in6p_flags &= ~(bit); \
+} while (/*CONSTCOND*/ 0)
#define OPTBIT(bit) (in6p->in6p_flags & (bit) ? 1 : 0)
- case IPV6_CHECKSUM:
- in6p->in6p_cksum = optval;
+ case IPV6_RECVPKTINFO:
+ /* cannot mix with RFC2292 */
+ if (OPTBIT(IN6P_RFC2292)) {
+ error = EINVAL;
+ break;
+ }
+ OPTSET(IN6P_PKTINFO);
+ break;
+
+ case IPV6_HOPLIMIT:
+ {
+ struct ip6_pktopts **optp;
+
+ /* cannot mix with RFC2292 */
+ if (OPTBIT(IN6P_RFC2292)) {
+ error = EINVAL;
+ break;
+ }
+ optp = &in6p->in6p_outputopts;
+ error = ip6_pcbopt(IPV6_HOPLIMIT,
+ (u_char *)&optval,
+ sizeof(optval),
+ optp,
+ privileged, uproto);
+ break;
+ }
+
+ case IPV6_RECVHOPLIMIT:
+ /* cannot mix with RFC2292 */
+ if (OPTBIT(IN6P_RFC2292)) {
+ error = EINVAL;
+ break;
+ }
+ OPTSET(IN6P_HOPLIMIT);
+ break;
+
+ case IPV6_RECVHOPOPTS:
+ /* cannot mix with RFC2292 */
+ if (OPTBIT(IN6P_RFC2292)) {
+ error = EINVAL;
+ break;
+ }
+ OPTSET(IN6P_HOPOPTS);
+ break;
+
+ case IPV6_RECVDSTOPTS:
+ /* cannot mix with RFC2292 */
+ if (OPTBIT(IN6P_RFC2292)) {
+ error = EINVAL;
+ break;
+ }
+ OPTSET(IN6P_DSTOPTS);
+ break;
+
+ case IPV6_RECVRTHDRDSTOPTS:
+ /* cannot mix with RFC2292 */
+ if (OPTBIT(IN6P_RFC2292)) {
+ error = EINVAL;
+ break;
+ }
+ OPTSET(IN6P_RTHDRDSTOPTS);
+ break;
+
+ case IPV6_RECVRTHDR:
+ /* cannot mix with RFC2292 */
+ if (OPTBIT(IN6P_RFC2292)) {
+ error = EINVAL;
+ break;
+ }
+ OPTSET(IN6P_RTHDR);
break;
case IPV6_FAITH:
OPTSET(IN6P_FAITH);
break;
+ case IPV6_RECVPATHMTU:
+ /*
+ * We ignore this option for TCP
+ * sockets.
+ * (rfc2292bis leaves this case
+ * unspecified.)
+ */
+ if (uproto != IPPROTO_TCP)
+ OPTSET(IN6P_MTU);
+ break;
+
case IPV6_V6ONLY:
/*
* make setsockopt(IPV6_V6ONLY)
@@ -1445,14 +1640,49 @@ do { \
else
in6p->in6p_vflag |= INP_IPV4;
break;
+ case IPV6_RECVTCLASS:
+ /* cannot mix with RFC2292 XXX */
+ if (OPTBIT(IN6P_RFC2292)) {
+ error = EINVAL;
+ break;
+ }
+ OPTSET(IN6P_TCLASS);
+ break;
+ case IPV6_AUTOFLOWLABEL:
+ OPTSET(IN6P_AUTOFLOWLABEL);
+ break;
+
}
break;
- case IPV6_PKTINFO:
- case IPV6_HOPLIMIT:
- case IPV6_HOPOPTS:
- case IPV6_DSTOPTS:
- case IPV6_RTHDR:
+ case IPV6_TCLASS:
+ case IPV6_DONTFRAG:
+ case IPV6_USE_MIN_MTU:
+ case IPV6_PREFER_TEMPADDR:
+ if (optlen != sizeof(optval)) {
+ error = EINVAL;
+ break;
+ }
+ error = sooptcopyin(sopt, &optval,
+ sizeof optval, sizeof optval);
+ if (error)
+ break;
+ {
+ struct ip6_pktopts **optp;
+ optp = &in6p->in6p_outputopts;
+ error = ip6_pcbopt(optname,
+ (u_char *)&optval,
+ sizeof(optval),
+ optp,
+ privileged, uproto);
+ break;
+ }
+
+ case IPV6_2292PKTINFO:
+ case IPV6_2292HOPLIMIT:
+ case IPV6_2292HOPOPTS:
+ case IPV6_2292DSTOPTS:
+ case IPV6_2292RTHDR:
/* RFC 2292 */
if (optlen != sizeof(int)) {
error = EINVAL;
@@ -1463,31 +1693,57 @@ do { \
if (error)
break;
switch (optname) {
- case IPV6_PKTINFO:
- OPTSET(IN6P_PKTINFO);
+ case IPV6_2292PKTINFO:
+ OPTSET2292(IN6P_PKTINFO);
break;
- case IPV6_HOPLIMIT:
- OPTSET(IN6P_HOPLIMIT);
+ case IPV6_2292HOPLIMIT:
+ OPTSET2292(IN6P_HOPLIMIT);
break;
- case IPV6_HOPOPTS:
+ case IPV6_2292HOPOPTS:
/*
* Check super-user privilege.
* See comments for IPV6_RECVHOPOPTS.
*/
if (!privileged)
return (EPERM);
- OPTSET(IN6P_HOPOPTS);
+ OPTSET2292(IN6P_HOPOPTS);
break;
- case IPV6_DSTOPTS:
+ case IPV6_2292DSTOPTS:
if (!privileged)
return (EPERM);
- OPTSET(IN6P_DSTOPTS|IN6P_RTHDRDSTOPTS); /* XXX */
+ OPTSET2292(IN6P_DSTOPTS|IN6P_RTHDRDSTOPTS); /* XXX */
break;
- case IPV6_RTHDR:
- OPTSET(IN6P_RTHDR);
+ case IPV6_2292RTHDR:
+ OPTSET2292(IN6P_RTHDR);
break;
}
break;
+ case IPV6_PKTINFO:
+ case IPV6_HOPOPTS:
+ case IPV6_RTHDR:
+ case IPV6_DSTOPTS:
+ case IPV6_RTHDRDSTOPTS:
+ case IPV6_NEXTHOP:
+ {
+ /* new advanced API (2292bis) */
+ u_char *optbuf;
+ int optlen;
+ struct ip6_pktopts **optp;
+
+ /* cannot mix with RFC2292 */
+ if (OPTBIT(IN6P_RFC2292)) {
+ error = EINVAL;
+ break;
+ }
+
+ optbuf = sopt->sopt_val;
+ optlen = sopt->sopt_valsize;
+ optp = &in6p->in6p_outputopts;
+ error = ip6_pcbopt(optname,
+ optbuf, optlen,
+ optp, privileged, uproto);
+ break;
+ }
#undef OPTSET
case IPV6_MULTICAST_IF:
@@ -1496,21 +1752,41 @@ do { \
case IPV6_JOIN_GROUP:
case IPV6_LEAVE_GROUP:
{
+ if (sopt->sopt_valsize > MLEN) {
+ error = EMSGSIZE;
+ break;
+ }
+ /* XXX */
+ }
+ /* FALLTHROUGH */
+ {
struct mbuf *m;
- if (sopt->sopt_valsize > MLEN) {
+ if (sopt->sopt_valsize > MCLBYTES) {
error = EMSGSIZE;
break;
}
/* XXX */
- MGET(m, sopt->sopt_td ? M_TRYWAIT : M_DONTWAIT, MT_HEADER);
+ MGET(m, sopt->sopt_td ? M_WAIT : M_DONTWAIT, MT_HEADER);
if (m == 0) {
error = ENOBUFS;
break;
}
+ if (sopt->sopt_valsize > MLEN) {
+ MCLGET(m, sopt->sopt_td ? M_WAIT : M_DONTWAIT);
+ if ((m->m_flags & M_EXT) == 0) {
+ m_free(m);
+ error = ENOBUFS;
+ break;
+ }
+ }
m->m_len = sopt->sopt_valsize;
error = sooptcopyin(sopt, mtod(m, char *),
m->m_len, m->m_len);
+ if (error) {
+ (void)m_free(m);
+ break;
+ }
error = ip6_setmoptions(sopt->sopt_name,
&in6p->in6p_moptions,
m);
@@ -1598,32 +1874,68 @@ do { \
case SOPT_GET:
switch (optname) {
+ case IPV6_2292PKTOPTIONS:
+#ifdef IPV6_PKTOPTIONS
case IPV6_PKTOPTIONS:
- if (in6p->in6p_options) {
- struct mbuf *m;
- m = m_copym(in6p->in6p_options,
- 0, M_COPYALL, M_TRYWAIT);
- error = soopt_mcopyout(sopt, m);
- if (error == 0)
- m_freem(m);
- } else
- sopt->sopt_valsize = 0;
+#endif
+ /*
+ * RFC3542 (effectively) deprecated the
+ * semantics of the 2292-style pktoptions.
+ * Since it was not reliable in nature (i.e.,
+ * applications had to expect the lack of some
+ * information after all), it would make sense
+ * to simplify this part by always returning
+ * empty data.
+ */
+ sopt->sopt_valsize = 0;
break;
+ case IPV6_RECVHOPOPTS:
+ case IPV6_RECVDSTOPTS:
+ case IPV6_RECVRTHDRDSTOPTS:
case IPV6_UNICAST_HOPS:
- case IPV6_CHECKSUM:
+ case IPV6_RECVPKTINFO:
+ case IPV6_RECVHOPLIMIT:
+ case IPV6_RECVRTHDR:
+ case IPV6_RECVPATHMTU:
case IPV6_FAITH:
case IPV6_V6ONLY:
case IPV6_PORTRANGE:
+ case IPV6_RECVTCLASS:
+ case IPV6_AUTOFLOWLABEL:
switch (optname) {
+ case IPV6_RECVHOPOPTS:
+ optval = OPTBIT(IN6P_HOPOPTS);
+ break;
+
+ case IPV6_RECVDSTOPTS:
+ optval = OPTBIT(IN6P_DSTOPTS);
+ break;
+
+ case IPV6_RECVRTHDRDSTOPTS:
+ optval = OPTBIT(IN6P_RTHDRDSTOPTS);
+ break;
+
case IPV6_UNICAST_HOPS:
optval = in6p->in6p_hops;
break;
- case IPV6_CHECKSUM:
- optval = in6p->in6p_cksum;
+ case IPV6_RECVPKTINFO:
+ optval = OPTBIT(IN6P_PKTINFO);
+ break;
+
+ case IPV6_RECVHOPLIMIT:
+ optval = OPTBIT(IN6P_HOPLIMIT);
+ break;
+
+ case IPV6_RECVRTHDR:
+ optval = OPTBIT(IN6P_RTHDR);
+ break;
+
+ case IPV6_RECVPATHMTU:
+ optval = OPTBIT(IN6P_MTU);
break;
case IPV6_FAITH:
@@ -1646,43 +1958,86 @@ do { \
optval = 0;
break;
}
+ case IPV6_RECVTCLASS:
+ optval = OPTBIT(IN6P_TCLASS);
+ break;
+
+ case IPV6_AUTOFLOWLABEL:
+ optval = OPTBIT(IN6P_AUTOFLOWLABEL);
+ break;
}
+ if (error)
+ break;
error = sooptcopyout(sopt, &optval,
sizeof optval);
break;
- case IPV6_PKTINFO:
- case IPV6_HOPLIMIT:
- case IPV6_HOPOPTS:
- case IPV6_RTHDR:
- case IPV6_DSTOPTS:
- if (optname == IPV6_HOPOPTS ||
- optname == IPV6_DSTOPTS ||
- !privileged)
- return (EPERM);
+ case IPV6_PATHMTU:
+ {
+ u_long pmtu = 0;
+ struct ip6_mtuinfo mtuinfo;
+ struct route_in6 *ro = (struct route_in6 *)&in6p->in6p_route;
+
+ if (!(so->so_state & SS_ISCONNECTED))
+ return (ENOTCONN);
+ /*
+ * XXX: we dot not consider the case of source
+ * routing, or optional information to specify
+ * the outgoing interface.
+ */
+ error = ip6_getpmtu(ro, NULL, NULL,
+ &in6p->in6p_faddr, &pmtu, NULL);
+ if (error)
+ break;
+ if (pmtu > IPV6_MAXPACKET)
+ pmtu = IPV6_MAXPACKET;
+
+ bzero(&mtuinfo, sizeof(mtuinfo));
+ mtuinfo.ip6m_mtu = (u_int32_t)pmtu;
+ optdata = (void *)&mtuinfo;
+ optdatalen = sizeof(mtuinfo);
+ error = sooptcopyout(sopt, optdata,
+ optdatalen);
+ break;
+ }
+
+ case IPV6_2292PKTINFO:
+ case IPV6_2292HOPLIMIT:
+ case IPV6_2292HOPOPTS:
+ case IPV6_2292RTHDR:
+ case IPV6_2292DSTOPTS:
switch (optname) {
- case IPV6_PKTINFO:
+ case IPV6_2292PKTINFO:
optval = OPTBIT(IN6P_PKTINFO);
break;
- case IPV6_HOPLIMIT:
+ case IPV6_2292HOPLIMIT:
optval = OPTBIT(IN6P_HOPLIMIT);
break;
- case IPV6_HOPOPTS:
- if (!privileged)
- return (EPERM);
+ case IPV6_2292HOPOPTS:
optval = OPTBIT(IN6P_HOPOPTS);
break;
- case IPV6_RTHDR:
+ case IPV6_2292RTHDR:
optval = OPTBIT(IN6P_RTHDR);
break;
- case IPV6_DSTOPTS:
- if (!privileged)
- return (EPERM);
+ case IPV6_2292DSTOPTS:
optval = OPTBIT(IN6P_DSTOPTS|IN6P_RTHDRDSTOPTS);
break;
}
error = sooptcopyout(sopt, &optval,
- sizeof optval);
+ sizeof optval);
+ break;
+ case IPV6_PKTINFO:
+ case IPV6_HOPOPTS:
+ case IPV6_RTHDR:
+ case IPV6_DSTOPTS:
+ case IPV6_RTHDRDSTOPTS:
+ case IPV6_NEXTHOP:
+ case IPV6_TCLASS:
+ case IPV6_DONTFRAG:
+ case IPV6_USE_MIN_MTU:
+ case IPV6_PREFER_TEMPADDR:
+ error = ip6_getpcbopt(in6p->in6p_outputopts,
+ optname, sopt);
break;
case IPV6_MULTICAST_IF:
@@ -1708,6 +2063,8 @@ do { \
size_t len = 0;
struct mbuf *m = NULL;
struct mbuf **mp = &m;
+ size_t ovalsize = sopt->sopt_valsize;
+ caddr_t oval = (caddr_t)sopt->sopt_val;
error = soopt_getm(sopt, &m); /* XXX */
if (error != NULL)
@@ -1715,6 +2072,8 @@ do { \
error = soopt_mcopyin(sopt, m); /* XXX */
if (error != NULL)
break;
+ sopt->sopt_valsize = ovalsize;
+ sopt->sopt_val = oval;
if (m) {
req = mtod(m, caddr_t);
len = m->m_len;
@@ -1751,7 +2110,7 @@ do { \
}
break;
}
- } else {
+ } else { /* level != IPPROTO_IPV6 */
error = EINVAL;
}
return (error);
@@ -1781,7 +2140,7 @@ ip6_pcbopts(pktopt, m, so, sopt)
opt->ip6po_rhinfo.ip6po_rhi_rthdr)
printf("ip6_pcbopts: all specified options are cleared.\n");
#endif
- ip6_clearpktopts(opt, 1, -1);
+ ip6_clearpktopts(opt, -1);
} else
opt = malloc(sizeof(*opt), M_IP6OPT, M_WAITOK);
*pktopt = NULL;
@@ -1798,8 +2157,9 @@ ip6_pcbopts(pktopt, m, so, sopt)
/* set options specified by user. */
if (td && !suser(td))
priv = 1;
- if ((error = ip6_setpktoptions(m, opt, priv, 1)) != 0) {
- ip6_clearpktopts(opt, 1, -1); /* XXX: discard all options */
+ if ((error = ip6_setpktoptions(m, opt, NULL, priv, 1,
+ so->so_proto->pr_protocol)) != 0) {
+ ip6_clearpktopts(opt, -1); /* XXX: discard all options */
free(opt, M_IP6OPT);
return (error);
}
@@ -1818,39 +2178,170 @@ init_ip6pktopts(opt)
bzero(opt, sizeof(*opt));
opt->ip6po_hlim = -1; /* -1 means default hop limit */
+ opt->ip6po_tclass = -1; /* -1 means default traffic class */
+ opt->ip6po_minmtu = IP6PO_MINMTU_MCASTONLY;
+ opt->ip6po_prefer_tempaddr = IP6PO_TEMPADDR_SYSTEM;
+}
+
+static int
+ip6_pcbopt(optname, buf, len, pktopt, priv, uproto)
+ int optname, len, priv;
+ u_char *buf;
+ struct ip6_pktopts **pktopt;
+ int uproto;
+{
+ struct ip6_pktopts *opt;
+
+ if (*pktopt == NULL) {
+ *pktopt = malloc(sizeof(struct ip6_pktopts), M_IP6OPT,
+ M_WAITOK);
+ init_ip6pktopts(*pktopt);
+ (*pktopt)->needfree = 1;
+ }
+ opt = *pktopt;
+
+ return (ip6_setpktoption(optname, buf, len, opt, priv, 1, 0, uproto));
+}
+
+static int
+ip6_getpcbopt(pktopt, optname, sopt)
+ struct ip6_pktopts *pktopt;
+ struct sockopt *sopt;
+ int optname;
+{
+ void *optdata = NULL;
+ int optdatalen = 0;
+ struct ip6_ext *ip6e;
+ int error = 0;
+ struct in6_pktinfo null_pktinfo;
+ int deftclass = 0, on;
+ int defminmtu = IP6PO_MINMTU_MCASTONLY;
+ int defpreftemp = IP6PO_TEMPADDR_SYSTEM;
+
+ switch (optname) {
+ case IPV6_PKTINFO:
+ if (pktopt && pktopt->ip6po_pktinfo)
+ optdata = (void *)pktopt->ip6po_pktinfo;
+ else {
+ /* XXX: we don't have to do this every time... */
+ bzero(&null_pktinfo, sizeof(null_pktinfo));
+ optdata = (void *)&null_pktinfo;
+ }
+ optdatalen = sizeof(struct in6_pktinfo);
+ break;
+ case IPV6_TCLASS:
+ if (pktopt && pktopt->ip6po_tclass >= 0)
+ optdata = (void *)&pktopt->ip6po_tclass;
+ else
+ optdata = (void *)&deftclass;
+ optdatalen = sizeof(int);
+ break;
+ case IPV6_HOPOPTS:
+ if (pktopt && pktopt->ip6po_hbh) {
+ optdata = (void *)pktopt->ip6po_hbh;
+ ip6e = (struct ip6_ext *)pktopt->ip6po_hbh;
+ optdatalen = (ip6e->ip6e_len + 1) << 3;
+ }
+ break;
+ case IPV6_RTHDR:
+ if (pktopt && pktopt->ip6po_rthdr) {
+ optdata = (void *)pktopt->ip6po_rthdr;
+ ip6e = (struct ip6_ext *)pktopt->ip6po_rthdr;
+ optdatalen = (ip6e->ip6e_len + 1) << 3;
+ }
+ break;
+ case IPV6_RTHDRDSTOPTS:
+ if (pktopt && pktopt->ip6po_dest1) {
+ optdata = (void *)pktopt->ip6po_dest1;
+ ip6e = (struct ip6_ext *)pktopt->ip6po_dest1;
+ optdatalen = (ip6e->ip6e_len + 1) << 3;
+ }
+ break;
+ case IPV6_DSTOPTS:
+ if (pktopt && pktopt->ip6po_dest2) {
+ optdata = (void *)pktopt->ip6po_dest2;
+ ip6e = (struct ip6_ext *)pktopt->ip6po_dest2;
+ optdatalen = (ip6e->ip6e_len + 1) << 3;
+ }
+ break;
+ case IPV6_NEXTHOP:
+ if (pktopt && pktopt->ip6po_nexthop) {
+ optdata = (void *)pktopt->ip6po_nexthop;
+ optdatalen = pktopt->ip6po_nexthop->sa_len;
+ }
+ break;
+ case IPV6_USE_MIN_MTU:
+ if (pktopt)
+ optdata = (void *)&pktopt->ip6po_minmtu;
+ else
+ optdata = (void *)&defminmtu;
+ optdatalen = sizeof(int);
+ break;
+ case IPV6_DONTFRAG:
+ if (pktopt && ((pktopt->ip6po_flags) & IP6PO_DONTFRAG))
+ on = 1;
+ else
+ on = 0;
+ optdata = (void *)&on;
+ optdatalen = sizeof(on);
+ break;
+ case IPV6_PREFER_TEMPADDR:
+ if (pktopt)
+ optdata = (void *)&pktopt->ip6po_prefer_tempaddr;
+ else
+ optdata = (void *)&defpreftemp;
+ optdatalen = sizeof(int);
+ break;
+ default: /* should not happen */
+#ifdef DIAGNOSTIC
+ panic("ip6_getpcbopt: unexpected option\n");
+#endif
+ return (ENOPROTOOPT);
+ }
+
+ error = sooptcopyout(sopt, optdata, optdatalen);
+
+ return (error);
}
void
-ip6_clearpktopts(pktopt, needfree, optname)
+ip6_clearpktopts(pktopt, optname)
struct ip6_pktopts *pktopt;
- int needfree, optname;
+ int optname;
{
- if (pktopt == NULL)
- return;
+ int needfree;
+
+ needfree = pktopt->needfree;
- if (optname == -1) {
+ if (optname == -1 || optname == IPV6_PKTINFO) {
if (needfree && pktopt->ip6po_pktinfo)
free(pktopt->ip6po_pktinfo, M_IP6OPT);
pktopt->ip6po_pktinfo = NULL;
}
- if (optname == -1)
+ if (optname == -1 || optname == IPV6_HOPLIMIT)
pktopt->ip6po_hlim = -1;
- if (optname == -1) {
+ if (optname == -1 || optname == IPV6_TCLASS)
+ pktopt->ip6po_tclass = -1;
+ if (optname == -1 || optname == IPV6_NEXTHOP) {
+ if (pktopt->ip6po_nextroute.ro_rt) {
+ RTFREE(pktopt->ip6po_nextroute.ro_rt);
+ pktopt->ip6po_nextroute.ro_rt = NULL;
+ }
if (needfree && pktopt->ip6po_nexthop)
free(pktopt->ip6po_nexthop, M_IP6OPT);
pktopt->ip6po_nexthop = NULL;
}
- if (optname == -1) {
+ if (optname == -1 || optname == IPV6_HOPOPTS) {
if (needfree && pktopt->ip6po_hbh)
free(pktopt->ip6po_hbh, M_IP6OPT);
pktopt->ip6po_hbh = NULL;
}
- if (optname == -1) {
+ if (optname == -1 || optname == IPV6_RTHDRDSTOPTS) {
if (needfree && pktopt->ip6po_dest1)
free(pktopt->ip6po_dest1, M_IP6OPT);
pktopt->ip6po_dest1 = NULL;
}
- if (optname == -1) {
+ if (optname == -1 || optname == IPV6_RTHDR) {
if (needfree && pktopt->ip6po_rhinfo.ip6po_rhi_rthdr)
free(pktopt->ip6po_rhinfo.ip6po_rhi_rthdr, M_IP6OPT);
pktopt->ip6po_rhinfo.ip6po_rhi_rthdr = NULL;
@@ -1859,7 +2350,7 @@ ip6_clearpktopts(pktopt, needfree, optname)
pktopt->ip6po_route.ro_rt = NULL;
}
}
- if (optname == -1) {
+ if (optname == -1 || optname == IPV6_DSTOPTS) {
if (needfree && pktopt->ip6po_dest2)
free(pktopt->ip6po_dest2, M_IP6OPT);
pktopt->ip6po_dest2 = NULL;
@@ -1893,8 +2384,11 @@ ip6_copypktopts(src, canwait)
if (dst == NULL && canwait == M_NOWAIT)
return (NULL);
bzero(dst, sizeof(*dst));
+ dst->needfree = 1;
dst->ip6po_hlim = src->ip6po_hlim;
+ dst->ip6po_tclass = src->ip6po_tclass;
+ dst->ip6po_flags = src->ip6po_flags;
if (src->ip6po_pktinfo) {
dst->ip6po_pktinfo = malloc(sizeof(*dst->ip6po_pktinfo),
M_IP6OPT, canwait);
@@ -1935,7 +2429,7 @@ ip6_freepcbopts(pktopt)
if (pktopt == NULL)
return;
- ip6_clearpktopts(pktopt, 1, -1);
+ ip6_clearpktopts(pktopt, -1);
free(pktopt, M_IP6OPT);
}
@@ -2300,17 +2794,33 @@ ip6_freemoptions(im6o)
* Set IPv6 outgoing packet options based on advanced API.
*/
int
-ip6_setpktoptions(control, opt, priv, needcopy)
+ip6_setpktoptions(control, opt, stickyopt, priv, needcopy, uproto)
struct mbuf *control;
- struct ip6_pktopts *opt;
- int priv, needcopy;
+ struct ip6_pktopts *opt, *stickyopt;
+ int priv, needcopy, uproto;
{
struct cmsghdr *cm = 0;
if (control == 0 || opt == 0)
return (EINVAL);
- init_ip6pktopts(opt);
+ if (stickyopt) {
+ /*
+ * If stickyopt is provided, make a local copy of the options
+ * for this particular packet, then override them by ancillary
+ * objects.
+ * XXX: need to gain a reference for the cached route of the
+ * next hop in case of the overriding.
+ */
+ *opt = *stickyopt;
+ if (opt->ip6po_nextroute.ro_rt) {
+ RT_LOCK(opt->ip6po_nextroute.ro_rt);
+ opt->ip6po_nextroute.ro_rt->rt_refcnt++;
+ RT_UNLOCK(opt->ip6po_nextroute.ro_rt);
+ }
+ } else
+ init_ip6pktopts(opt);
+ opt->needfree = needcopy;
/*
* XXX: Currently, we assume all the optional information is stored
@@ -2321,192 +2831,421 @@ ip6_setpktoptions(control, opt, priv, needcopy)
for (; control->m_len; control->m_data += CMSG_ALIGN(cm->cmsg_len),
control->m_len -= CMSG_ALIGN(cm->cmsg_len)) {
+ int error;
+
+ if (control->m_len < CMSG_LEN(0))
+ return (EINVAL);
+
cm = mtod(control, struct cmsghdr *);
if (cm->cmsg_len == 0 || cm->cmsg_len > control->m_len)
return (EINVAL);
if (cm->cmsg_level != IPPROTO_IPV6)
continue;
+ error = ip6_setpktoption(cm->cmsg_type, CMSG_DATA(cm),
+ cm->cmsg_len - CMSG_LEN(0), opt, priv, needcopy, 1, uproto);
+ if (error)
+ return (error);
+ }
+
+ return (0);
+}
+
+/*
+ * Set a particular packet option, as a sticky option or an ancillary data
+ * item. "len" can be 0 only when it's a sticky option.
+ * We have 4 cases of combination of "sticky" and "cmsg":
+ * "sticky=0, cmsg=0": impossible
+ * "sticky=0, cmsg=1": RFC2292 or rfc2292bis ancillary data
+ * "sticky=1, cmsg=0": rfc2292bis socket option
+ * "sticky=1, cmsg=1": RFC2292 socket option
+ */
+static int
+ip6_setpktoption(optname, buf, len, opt, priv, sticky, cmsg, uproto)
+ int optname, len, priv, sticky, cmsg, uproto;
+ u_char *buf;
+ struct ip6_pktopts *opt;
+{
+ int minmtupolicy, preftemp;
+
+ if (!sticky && !cmsg) {
+#ifdef DIAGNOSTIC
+ printf("ip6_setpktoption: impossible case\n");
+#endif
+ return (EINVAL);
+ }
+
+ /*
+ * IPV6_2292xxx is for backward compatibility to RFC2292, and should
+ * not be specified in the context of rfc2292bis. Conversely,
+ * rfc2292bis types should not be specified in the context of RFC2292.
+ */
+ if (!cmsg) {
+ switch (optname) {
+ case IPV6_2292PKTINFO:
+ case IPV6_2292HOPLIMIT:
+ case IPV6_2292NEXTHOP:
+ case IPV6_2292HOPOPTS:
+ case IPV6_2292DSTOPTS:
+ case IPV6_2292RTHDR:
+ case IPV6_2292PKTOPTIONS:
+ return (ENOPROTOOPT);
+ }
+ }
+ if (sticky && cmsg) {
+ switch (optname) {
+ case IPV6_PKTINFO:
+ case IPV6_HOPLIMIT:
+ case IPV6_NEXTHOP:
+ case IPV6_HOPOPTS:
+ case IPV6_DSTOPTS:
+ case IPV6_RTHDRDSTOPTS:
+ case IPV6_RTHDR:
+ case IPV6_USE_MIN_MTU:
+ case IPV6_DONTFRAG:
+ case IPV6_TCLASS:
+ case IPV6_PREFER_TEMPADDR: /* XXX: not an rfc2292bis option */
+ return (ENOPROTOOPT);
+ }
+ }
+
+ switch (optname) {
+ case IPV6_2292PKTINFO:
+ case IPV6_PKTINFO:
+ {
+ struct ifnet *ifp = NULL;
+ struct in6_pktinfo *pktinfo;
+
+ if (len != sizeof(struct in6_pktinfo))
+ return (EINVAL);
+
+ pktinfo = (struct in6_pktinfo *)buf;
+
/*
- * XXX should check if RFC2292 API is mixed with 2292bis API
+ * An application can clear any sticky IPV6_PKTINFO option by
+ * doing a "regular" setsockopt with ipi6_addr being
+ * in6addr_any and ipi6_ifindex being zero.
+ * [RFC 3542, Section 6]
*/
- switch (cm->cmsg_type) {
- case IPV6_PKTINFO:
- if (cm->cmsg_len != CMSG_LEN(sizeof(struct in6_pktinfo)))
- return (EINVAL);
- if (needcopy) {
- /* XXX: Is it really WAITOK? */
- opt->ip6po_pktinfo =
- malloc(sizeof(struct in6_pktinfo),
- M_IP6OPT, M_WAITOK);
- bcopy(CMSG_DATA(cm), opt->ip6po_pktinfo,
- sizeof(struct in6_pktinfo));
- } else
- opt->ip6po_pktinfo =
- (struct in6_pktinfo *)CMSG_DATA(cm);
- if (opt->ip6po_pktinfo->ipi6_ifindex &&
- IN6_IS_ADDR_LINKLOCAL(&opt->ip6po_pktinfo->ipi6_addr))
- opt->ip6po_pktinfo->ipi6_addr.s6_addr16[1] =
- htons(opt->ip6po_pktinfo->ipi6_ifindex);
-
- if (opt->ip6po_pktinfo->ipi6_ifindex > if_index
- || opt->ip6po_pktinfo->ipi6_ifindex < 0) {
+ if (optname == IPV6_PKTINFO && opt->ip6po_pktinfo &&
+ pktinfo->ipi6_ifindex == 0 &&
+ IN6_IS_ADDR_UNSPECIFIED(&pktinfo->ipi6_addr)) {
+ ip6_clearpktopts(opt, optname);
+ break;
+ }
+
+ if (uproto == IPPROTO_TCP && optname == IPV6_PKTINFO &&
+ sticky && !IN6_IS_ADDR_UNSPECIFIED(&pktinfo->ipi6_addr)) {
+ return (EINVAL);
+ }
+
+ /* validate the interface index if specified. */
+ if (pktinfo->ipi6_ifindex > if_index ||
+ pktinfo->ipi6_ifindex < 0) {
+ return (ENXIO);
+ }
+ if (pktinfo->ipi6_ifindex) {
+ ifp = ifnet_byindex(pktinfo->ipi6_ifindex);
+ if (ifp == NULL)
return (ENXIO);
- }
+ }
- /*
- * Check if the requested source address is indeed a
- * unicast address assigned to the node, and can be
- * used as the packet's source address.
- */
- if (!IN6_IS_ADDR_UNSPECIFIED(&opt->ip6po_pktinfo->ipi6_addr)) {
- struct in6_ifaddr *ia6;
- struct sockaddr_in6 sin6;
-
- bzero(&sin6, sizeof(sin6));
- sin6.sin6_len = sizeof(sin6);
- sin6.sin6_family = AF_INET6;
- sin6.sin6_addr =
- opt->ip6po_pktinfo->ipi6_addr;
- ia6 = (struct in6_ifaddr *)ifa_ifwithaddr(sin6tosa(&sin6));
- if (ia6 == NULL ||
- (ia6->ia6_flags & (IN6_IFF_ANYCAST |
- IN6_IFF_NOTREADY)) != 0)
- return (EADDRNOTAVAIL);
+ /*
+ * We store the address anyway, and let in6_selectsrc()
+ * validate the specified address. This is because ipi6_addr
+ * may not have enough information about its scope zone, and
+ * we may need additional information (such as outgoing
+ * interface or the scope zone of a destination address) to
+ * disambiguate the scope.
+ * XXX: the delay of the validation may confuse the
+ * application when it is used as a sticky option.
+ */
+ if (sticky) {
+ if (opt->ip6po_pktinfo == NULL) {
+ opt->ip6po_pktinfo = malloc(sizeof(*pktinfo),
+ M_IP6OPT, M_WAITOK);
}
- break;
+ bcopy(pktinfo, opt->ip6po_pktinfo, sizeof(*pktinfo));
+ } else
+ opt->ip6po_pktinfo = pktinfo;
+ break;
+ }
- case IPV6_HOPLIMIT:
- if (cm->cmsg_len != CMSG_LEN(sizeof(int)))
- return (EINVAL);
+ case IPV6_2292HOPLIMIT:
+ case IPV6_HOPLIMIT:
+ {
+ int *hlimp;
- opt->ip6po_hlim = *(int *)CMSG_DATA(cm);
- if (opt->ip6po_hlim < -1 || opt->ip6po_hlim > 255)
- return (EINVAL);
- break;
+ /*
+ * RFC 3542 deprecated the usage of sticky IPV6_HOPLIMIT
+ * to simplify the ordering among hoplimit options.
+ */
+ if (optname == IPV6_HOPLIMIT && sticky)
+ return (ENOPROTOOPT);
- case IPV6_NEXTHOP:
- if (!priv)
- return (EPERM);
+ if (len != sizeof(int))
+ return (EINVAL);
+ hlimp = (int *)buf;
+ if (*hlimp < -1 || *hlimp > 255)
+ return (EINVAL);
- if (cm->cmsg_len < sizeof(u_char) ||
- /* check if cmsg_len is large enough for sa_len */
- cm->cmsg_len < CMSG_LEN(*CMSG_DATA(cm)))
- return (EINVAL);
+ opt->ip6po_hlim = *hlimp;
+ break;
+ }
- if (needcopy) {
- opt->ip6po_nexthop =
- malloc(*CMSG_DATA(cm),
- M_IP6OPT, M_WAITOK);
- bcopy(CMSG_DATA(cm),
- opt->ip6po_nexthop,
- *CMSG_DATA(cm));
- } else
- opt->ip6po_nexthop =
- (struct sockaddr *)CMSG_DATA(cm);
- break;
+ case IPV6_TCLASS:
+ {
+ int tclass;
- case IPV6_HOPOPTS:
- {
- struct ip6_hbh *hbh;
- int hbhlen;
+ if (len != sizeof(int))
+ return (EINVAL);
+ tclass = *(int *)buf;
+ if (tclass < -1 || tclass > 255)
+ return (EINVAL);
- if (cm->cmsg_len < CMSG_LEN(sizeof(struct ip6_hbh)))
- return (EINVAL);
- hbh = (struct ip6_hbh *)CMSG_DATA(cm);
- hbhlen = (hbh->ip6h_len + 1) << 3;
- if (cm->cmsg_len != CMSG_LEN(hbhlen))
- return (EINVAL);
+ opt->ip6po_tclass = tclass;
+ break;
+ }
- if (needcopy) {
- opt->ip6po_hbh =
- malloc(hbhlen, M_IP6OPT, M_WAITOK);
- bcopy(hbh, opt->ip6po_hbh, hbhlen);
- } else
- opt->ip6po_hbh = hbh;
+ case IPV6_2292NEXTHOP:
+ case IPV6_NEXTHOP:
+ if (!priv)
+ return (EPERM);
+
+ if (len == 0) { /* just remove the option */
+ ip6_clearpktopts(opt, IPV6_NEXTHOP);
break;
}
- case IPV6_DSTOPTS:
+ /* check if cmsg_len is large enough for sa_len */
+ if (len < sizeof(struct sockaddr) || len < *buf)
+ return (EINVAL);
+
+ switch (((struct sockaddr *)buf)->sa_family) {
+ case AF_INET6:
{
- struct ip6_dest *dest, **newdest;
- int destlen;
+ struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)buf;
+#if 0
+ int error;
+#endif
- if (cm->cmsg_len < CMSG_LEN(sizeof(struct ip6_dest)))
+ if (sa6->sin6_len != sizeof(struct sockaddr_in6))
return (EINVAL);
- dest = (struct ip6_dest *)CMSG_DATA(cm);
- destlen = (dest->ip6d_len + 1) << 3;
- if (cm->cmsg_len != CMSG_LEN(destlen))
+
+ if (IN6_IS_ADDR_UNSPECIFIED(&sa6->sin6_addr) ||
+ IN6_IS_ADDR_MULTICAST(&sa6->sin6_addr)) {
return (EINVAL);
+ }
+#if 0
+ if ((error = scope6_check_id(sa6, ip6_use_defzone))
+ != 0) {
+ return (error);
+ }
+#endif
+ sa6->sin6_scope_id = 0; /* XXX */
+ break;
+ }
+ case AF_LINK: /* should eventually be supported */
+ default:
+ return (EAFNOSUPPORT);
+ }
+
+ /* turn off the previous option, then set the new option. */
+ ip6_clearpktopts(opt, IPV6_NEXTHOP);
+ if (sticky) {
+ opt->ip6po_nexthop = malloc(*buf, M_IP6OPT, M_WAITOK);
+ bcopy(buf, opt->ip6po_nexthop, *buf);
+ } else
+ opt->ip6po_nexthop = (struct sockaddr *)buf;
+ break;
+
+ case IPV6_2292HOPOPTS:
+ case IPV6_HOPOPTS:
+ {
+ struct ip6_hbh *hbh;
+ int hbhlen;
+
+ /*
+ * XXX: We don't allow a non-privileged user to set ANY HbH
+ * options, since per-option restriction has too much
+ * overhead.
+ */
+ if (!priv)
+ return (EPERM);
+
+ if (len == 0) {
+ ip6_clearpktopts(opt, IPV6_HOPOPTS);
+ break; /* just remove the option */
+ }
+
+ /* message length validation */
+ if (len < sizeof(struct ip6_hbh))
+ return (EINVAL);
+ hbh = (struct ip6_hbh *)buf;
+ hbhlen = (hbh->ip6h_len + 1) << 3;
+ if (len != hbhlen)
+ return (EINVAL);
+
+ /* turn off the previous option, then set the new option. */
+ ip6_clearpktopts(opt, IPV6_HOPOPTS);
+ if (sticky) {
+ opt->ip6po_hbh = malloc(hbhlen, M_IP6OPT, M_WAITOK);
+ bcopy(hbh, opt->ip6po_hbh, hbhlen);
+ } else
+ opt->ip6po_hbh = hbh;
+
+ break;
+ }
+
+ case IPV6_2292DSTOPTS:
+ case IPV6_DSTOPTS:
+ case IPV6_RTHDRDSTOPTS:
+ {
+ struct ip6_dest *dest, **newdest = NULL;
+ int destlen;
+
+ if (!priv) /* XXX: see the comment for IPV6_HOPOPTS */
+ return (EPERM);
+
+ if (len == 0) {
+ ip6_clearpktopts(opt, optname);
+ break; /* just remove the option */
+ }
- /*
- * The old advacned API is ambiguous on this
- * point. Our approach is to determine the
- * position based according to the existence
- * of a routing header. Note, however, that
- * this depends on the order of the extension
- * headers in the ancillary data; the 1st part
- * of the destination options header must
- * appear before the routing header in the
- * ancillary data, too.
- * RFC2292bis solved the ambiguity by
- * introducing separate cmsg types.
+ /* message length validation */
+ if (len < sizeof(struct ip6_dest))
+ return (EINVAL);
+ dest = (struct ip6_dest *)buf;
+ destlen = (dest->ip6d_len + 1) << 3;
+ if (len != destlen)
+ return (EINVAL);
+
+ /*
+ * Determine the position that the destination options header
+ * should be inserted; before or after the routing header.
+ */
+ switch (optname) {
+ case IPV6_2292DSTOPTS:
+ /*
+ * The old advacned API is ambiguous on this point.
+ * Our approach is to determine the position based
+ * according to the existence of a routing header.
+ * Note, however, that this depends on the order of the
+ * extension headers in the ancillary data; the 1st
+ * part of the destination options header must appear
+ * before the routing header in the ancillary data,
+ * too.
+ * RFC2292bis solved the ambiguity by introducing
+ * separate ancillary data or option types.
*/
if (opt->ip6po_rthdr == NULL)
newdest = &opt->ip6po_dest1;
else
newdest = &opt->ip6po_dest2;
+ break;
+ case IPV6_RTHDRDSTOPTS:
+ newdest = &opt->ip6po_dest1;
+ break;
+ case IPV6_DSTOPTS:
+ newdest = &opt->ip6po_dest2;
+ break;
+ }
- if (needcopy) {
- *newdest = malloc(destlen, M_IP6OPT, M_WAITOK);
- bcopy(dest, *newdest, destlen);
- } else
- *newdest = dest;
+ /* turn off the previous option, then set the new option. */
+ ip6_clearpktopts(opt, optname);
+ if (sticky) {
+ *newdest = malloc(destlen, M_IP6OPT, M_WAITOK);
+ bcopy(dest, *newdest, destlen);
+ } else
+ *newdest = dest;
- break;
+ break;
+ }
+
+ case IPV6_2292RTHDR:
+ case IPV6_RTHDR:
+ {
+ struct ip6_rthdr *rth;
+ int rthlen;
+
+ if (len == 0) {
+ ip6_clearpktopts(opt, IPV6_RTHDR);
+ break; /* just remove the option */
}
- case IPV6_RTHDR:
- {
- struct ip6_rthdr *rth;
- int rthlen;
+ /* message length validation */
+ if (len < sizeof(struct ip6_rthdr))
+ return (EINVAL);
+ rth = (struct ip6_rthdr *)buf;
+ rthlen = (rth->ip6r_len + 1) << 3;
+ if (len != rthlen)
+ return (EINVAL);
- if (cm->cmsg_len < CMSG_LEN(sizeof(struct ip6_rthdr)))
+ switch (rth->ip6r_type) {
+ case IPV6_RTHDR_TYPE_0:
+ if (rth->ip6r_len == 0) /* must contain one addr */
return (EINVAL);
- rth = (struct ip6_rthdr *)CMSG_DATA(cm);
- rthlen = (rth->ip6r_len + 1) << 3;
- if (cm->cmsg_len != CMSG_LEN(rthlen))
+ if (rth->ip6r_len % 2) /* length must be even */
return (EINVAL);
+ if (rth->ip6r_len / 2 != rth->ip6r_segleft)
+ return (EINVAL);
+ break;
+ default:
+ return (EINVAL); /* not supported */
+ }
- switch (rth->ip6r_type) {
- case IPV6_RTHDR_TYPE_0:
- /* must contain one addr */
- if (rth->ip6r_len == 0)
- return (EINVAL);
- /* length must be even */
- if (rth->ip6r_len % 2)
- return (EINVAL);
- if (rth->ip6r_len / 2 != rth->ip6r_segleft)
- return (EINVAL);
- break;
- default:
- return (EINVAL); /* not supported */
- }
+ /* turn off the previous option */
+ ip6_clearpktopts(opt, IPV6_RTHDR);
+ if (sticky) {
+ opt->ip6po_rthdr = malloc(rthlen, M_IP6OPT, M_WAITOK);
+ bcopy(rth, opt->ip6po_rthdr, rthlen);
+ } else
+ opt->ip6po_rthdr = rth;
- if (needcopy) {
- opt->ip6po_rthdr = malloc(rthlen, M_IP6OPT,
- M_WAITOK);
- bcopy(rth, opt->ip6po_rthdr, rthlen);
- } else
- opt->ip6po_rthdr = rth;
+ break;
+ }
- break;
+ case IPV6_USE_MIN_MTU:
+ if (len != sizeof(int))
+ return (EINVAL);
+ minmtupolicy = *(int *)buf;
+ if (minmtupolicy != IP6PO_MINMTU_MCASTONLY &&
+ minmtupolicy != IP6PO_MINMTU_DISABLE &&
+ minmtupolicy != IP6PO_MINMTU_ALL) {
+ return (EINVAL);
}
+ opt->ip6po_minmtu = minmtupolicy;
+ break;
- default:
- return (ENOPROTOOPT);
+ case IPV6_DONTFRAG:
+ if (len != sizeof(int))
+ return (EINVAL);
+
+ if (uproto == IPPROTO_TCP || *(int *)buf == 0) {
+ /*
+ * we ignore this option for TCP sockets.
+ * (rfc2292bis leaves this case unspecified.)
+ */
+ opt->ip6po_flags &= ~IP6PO_DONTFRAG;
+ } else
+ opt->ip6po_flags |= IP6PO_DONTFRAG;
+ break;
+
+ case IPV6_PREFER_TEMPADDR:
+ if (len != sizeof(int))
+ return (EINVAL);
+ preftemp = *(int *)buf;
+ if (preftemp != IP6PO_TEMPADDR_SYSTEM &&
+ preftemp != IP6PO_TEMPADDR_NOTPREFER &&
+ preftemp != IP6PO_TEMPADDR_PREFER) {
+ return (EINVAL);
}
- }
+ opt->ip6po_prefer_tempaddr = preftemp;
+ break;
+
+ default:
+ return (ENOPROTOOPT);
+ } /* end of switch */
return (0);
}
diff --git a/sys/netinet6/ip6_var.h b/sys/netinet6/ip6_var.h
index dc96ed7..641914d 100644
--- a/sys/netinet6/ip6_var.h
+++ b/sys/netinet6/ip6_var.h
@@ -128,6 +128,14 @@ struct ip6po_rhinfo {
#define ip6po_rthdr ip6po_rhinfo.ip6po_rhi_rthdr
#define ip6po_route ip6po_rhinfo.ip6po_rhi_route
+/* Nexthop related info */
+struct ip6po_nhinfo {
+ struct sockaddr *ip6po_nhi_nexthop;
+ struct route_in6 ip6po_nhi_route; /* Route to the nexthop */
+};
+#define ip6po_nexthop ip6po_nhinfo.ip6po_nhi_nexthop
+#define ip6po_nextroute ip6po_nhinfo.ip6po_nhi_route
+
struct ip6_pktopts {
struct mbuf *ip6po_m; /* Pointer to mbuf storing the data */
int ip6po_hlim; /* Hoplimit for outgoing packets */
@@ -135,8 +143,9 @@ struct ip6_pktopts {
/* Outgoing IF/address information */
struct in6_pktinfo *ip6po_pktinfo;
- struct sockaddr *ip6po_nexthop; /* Next-hop address */
-
+ /* Next-hop address information */
+ struct ip6po_nhinfo ip6po_nhinfo;
+
struct ip6_hbh *ip6po_hbh; /* Hop-by-Hop options header */
/* Destination options header (before a routing header) */
@@ -147,6 +156,29 @@ struct ip6_pktopts {
/* Destination options header (after a routing header) */
struct ip6_dest *ip6po_dest2;
+
+ int ip6po_tclass; /* traffic class */
+
+ int ip6po_minmtu; /* fragment vs PMTU discovery policy */
+#define IP6PO_MINMTU_MCASTONLY -1 /* default; send at min MTU for multicast*/
+#define IP6PO_MINMTU_DISABLE 0 /* always perform pmtu disc */
+#define IP6PO_MINMTU_ALL 1 /* always send at min MTU */
+
+ int ip6po_prefer_tempaddr; /* whether temporary addresses are
+ preferred as source address */
+#define IP6PO_TEMPADDR_SYSTEM -1 /* follow the system default */
+#define IP6PO_TEMPADDR_NOTPREFER 0 /* not prefer temporary address */
+#define IP6PO_TEMPADDR_PREFER 1 /* prefer temporary address */
+
+ int ip6po_flags;
+#if 0 /* parameters in this block is obsolete. do not reuse the values. */
+#define IP6PO_REACHCONF 0x01 /* upper-layer reachability confirmation. */
+#define IP6PO_MINMTU 0x02 /* use minimum MTU (IPV6_USE_MIN_MTU) */
+#endif
+#define IP6PO_DONTFRAG 0x04 /* disable fragmentation (IPV6_DONTFRAG) */
+#define IP6PO_USECOA 0x08 /* use care of address */
+
+ int needfree; /* members dynamically allocated */
};
/*
@@ -336,8 +368,9 @@ int ip6_output __P((struct mbuf *, struct ip6_pktopts *,
struct inpcb *));
int ip6_ctloutput __P((struct socket *, struct sockopt *));
void init_ip6pktopts __P((struct ip6_pktopts *));
-int ip6_setpktoptions __P((struct mbuf *, struct ip6_pktopts *, int, int));
-void ip6_clearpktopts __P((struct ip6_pktopts *, int, int));
+int ip6_setpktoptions __P((struct mbuf *, struct ip6_pktopts *,
+ struct ip6_pktopts *, int, int, int));
+void ip6_clearpktopts __P((struct ip6_pktopts *, int));
struct ip6_pktopts *ip6_copypktopts __P((struct ip6_pktopts *, int));
int ip6_optlen __P((struct inpcb *));
diff --git a/sys/netinet6/mld6.c b/sys/netinet6/mld6.c
index 15df24d..e80c251 100644
--- a/sys/netinet6/mld6.c
+++ b/sys/netinet6/mld6.c
@@ -125,7 +125,7 @@ mld6_init()
/* XXX: grotty hard coding... */
hbh_buf[2] = IP6OPT_PADN; /* 2 byte padding */
hbh_buf[3] = 0;
- hbh_buf[4] = IP6OPT_RTALERT;
+ hbh_buf[4] = IP6OPT_ROUTER_ALERT;
hbh_buf[5] = IP6OPT_RTALERT_LEN - 2;
bcopy((caddr_t)&rtalert_code, &hbh_buf[6], sizeof(u_int16_t));
diff --git a/sys/netinet6/nd6.c b/sys/netinet6/nd6.c
index 7a74cdf..97b57e1 100644
--- a/sys/netinet6/nd6.c
+++ b/sys/netinet6/nd6.c
@@ -1323,7 +1323,7 @@ nd6_ioctl(cmd, data, ifp)
struct ifnet *ifp;
{
struct in6_drlist *drl = (struct in6_drlist *)data;
- struct in6_prlist *prl = (struct in6_prlist *)data;
+ struct in6_oprlist *oprl = (struct in6_oprlist *)data;
struct in6_ndireq *ndi = (struct in6_ndireq *)data;
struct in6_nbrinfo *nbi = (struct in6_nbrinfo *)data;
struct in6_ndifreq *ndif = (struct in6_ndifreq *)data;
@@ -1343,14 +1343,7 @@ nd6_ioctl(cmd, data, ifp)
dr = TAILQ_FIRST(&nd_defrouter);
while (dr && i < DRLSTSIZ) {
drl->defrouter[i].rtaddr = dr->rtaddr;
- if (IN6_IS_ADDR_LINKLOCAL(&drl->defrouter[i].rtaddr)) {
- /* XXX: need to this hack for KAME stack */
- drl->defrouter[i].rtaddr.s6_addr16[1] = 0;
- } else
- log(LOG_ERR,
- "default router list contains a "
- "non-linklocal address(%s)\n",
- ip6_sprintf(&drl->defrouter[i].rtaddr));
+ in6_clearscope(&drl->defrouter[i].rtaddr);
drl->defrouter[i].flags = dr->flags;
drl->defrouter[i].rtlifetime = dr->rtlifetime;
@@ -1364,50 +1357,46 @@ nd6_ioctl(cmd, data, ifp)
case SIOCGPRLST_IN6:
/*
* obsolete API, use sysctl under net.inet6.icmp6
+ *
+ * XXX the structure in6_prlist was changed in backward-
+ * incompatible manner. in6_oprlist is used for SIOCGPRLST_IN6,
+ * in6_prlist is used for nd6_sysctl() - fill_prlist().
*/
/*
* XXX meaning of fields, especialy "raflags", is very
* differnet between RA prefix list and RR/static prefix list.
* how about separating ioctls into two?
*/
- bzero(prl, sizeof(*prl));
+ bzero(oprl, sizeof(*oprl));
s = splnet();
pr = nd_prefix.lh_first;
while (pr && i < PRLSTSIZ) {
struct nd_pfxrouter *pfr;
int j;
- (void)in6_embedscope(&prl->prefix[i].prefix,
+ (void)in6_embedscope(&oprl->prefix[i].prefix,
&pr->ndpr_prefix, NULL, NULL);
- prl->prefix[i].raflags = pr->ndpr_raf;
- prl->prefix[i].prefixlen = pr->ndpr_plen;
- prl->prefix[i].vltime = pr->ndpr_vltime;
- prl->prefix[i].pltime = pr->ndpr_pltime;
- prl->prefix[i].if_index = pr->ndpr_ifp->if_index;
- prl->prefix[i].expire = pr->ndpr_expire;
+ oprl->prefix[i].raflags = pr->ndpr_raf;
+ oprl->prefix[i].prefixlen = pr->ndpr_plen;
+ oprl->prefix[i].vltime = pr->ndpr_vltime;
+ oprl->prefix[i].pltime = pr->ndpr_pltime;
+ oprl->prefix[i].if_index = pr->ndpr_ifp->if_index;
+ oprl->prefix[i].expire = pr->ndpr_expire;
pfr = pr->ndpr_advrtrs.lh_first;
j = 0;
while (pfr) {
if (j < DRLSTSIZ) {
-#define RTRADDR prl->prefix[i].advrtr[j]
+#define RTRADDR oprl->prefix[i].advrtr[j]
RTRADDR = pfr->router->rtaddr;
- if (IN6_IS_ADDR_LINKLOCAL(&RTRADDR)) {
- /* XXX: hack for KAME */
- RTRADDR.s6_addr16[1] = 0;
- } else
- log(LOG_ERR,
- "a router(%s) advertises "
- "a prefix with "
- "non-link local address\n",
- ip6_sprintf(&RTRADDR));
+ in6_clearscope(&RTRADDR);
#undef RTRADDR
}
j++;
pfr = pfr->pfr_next;
}
- prl->prefix[i].advrtrs = j;
- prl->prefix[i].origin = PR_ORIG_RA;
+ oprl->prefix[i].advrtrs = j;
+ oprl->prefix[i].origin = PR_ORIG_RA;
i++;
pr = pr->ndpr_next;
@@ -1419,16 +1408,16 @@ nd6_ioctl(cmd, data, ifp)
rpp = LIST_NEXT(rpp, rp_entry)) {
if (i >= PRLSTSIZ)
break;
- (void)in6_embedscope(&prl->prefix[i].prefix,
+ (void)in6_embedscope(&oprl->prefix[i].prefix,
&pr->ndpr_prefix, NULL, NULL);
- prl->prefix[i].raflags = rpp->rp_raf;
- prl->prefix[i].prefixlen = rpp->rp_plen;
- prl->prefix[i].vltime = rpp->rp_vltime;
- prl->prefix[i].pltime = rpp->rp_pltime;
- prl->prefix[i].if_index = rpp->rp_ifp->if_index;
- prl->prefix[i].expire = rpp->rp_expire;
- prl->prefix[i].advrtrs = 0;
- prl->prefix[i].origin = rpp->rp_origin;
+ oprl->prefix[i].raflags = rpp->rp_raf;
+ oprl->prefix[i].prefixlen = rpp->rp_plen;
+ oprl->prefix[i].vltime = rpp->rp_vltime;
+ oprl->prefix[i].pltime = rpp->rp_pltime;
+ oprl->prefix[i].if_index = rpp->rp_ifp->if_index;
+ oprl->prefix[i].expire = rpp->rp_expire;
+ oprl->prefix[i].advrtrs = 0;
+ oprl->prefix[i].origin = rpp->rp_origin;
i++;
}
}
diff --git a/sys/netinet6/nd6.h b/sys/netinet6/nd6.h
index 5762312..c60dc60 100644
--- a/sys/netinet6/nd6.h
+++ b/sys/netinet6/nd6.h
@@ -129,6 +129,24 @@ struct in6_defrouter {
u_short if_index;
};
+#ifdef _KERNEL
+struct in6_oprlist {
+ char ifname[IFNAMSIZ];
+ struct {
+ struct in6_addr prefix;
+ struct prf_ra raflags;
+ u_char prefixlen;
+ u_char origin;
+ u_long vltime;
+ u_long pltime;
+ u_long expire;
+ u_short if_index;
+ u_short advrtrs; /* number of advertisement routers */
+ struct in6_addr advrtr[DRLSTSIZ]; /* XXX: explicit limit */
+ } prefix[PRLSTSIZ];
+};
+#endif
+
struct in6_prlist {
char ifname[IFNAMSIZ];
struct {
@@ -150,9 +168,9 @@ struct in6_prefix {
struct prf_ra raflags;
u_char prefixlen;
u_char origin;
- u_long vltime;
- u_long pltime;
- u_long expire;
+ u_int32_t vltime;
+ u_int32_t pltime;
+ time_t expire;
u_int32_t flags;
int refcnt;
u_short if_index;
@@ -220,9 +238,6 @@ struct nd_defrouter {
u_char flags; /* flags on RA message */
u_short rtlifetime;
u_long expire;
- u_long advint; /* Mobile IPv6 addition (milliseconds) */
- u_long advint_expire; /* Mobile IPv6 addition */
- int advints_lost; /* Mobile IPv6 addition */
struct ifnet *ifp;
};
@@ -319,7 +334,7 @@ extern u_int32_t ip6_temp_valid_lifetime; /* seconds */
extern int ip6_temp_regen_advance; /* seconds */
union nd_opts {
- struct nd_opt_hdr *nd_opt_array[9]; /* max = home agent info */
+ struct nd_opt_hdr *nd_opt_array[13]; /* max = target address list */
struct {
struct nd_opt_hdr *zero;
struct nd_opt_hdr *src_lladdr;
@@ -328,8 +343,10 @@ union nd_opts {
struct nd_opt_rd_hdr *rh;
struct nd_opt_mtu *mtu;
struct nd_opt_hdr *six;
- struct nd_opt_advint *adv;
- struct nd_opt_hai *hai;
+ struct nd_opt_advinterval *adv;
+ struct nd_opt_homeagent_info *hai;
+ struct nd_opt_hdr *src_addrlist;
+ struct nd_opt_hdr *tgt_addrlist;
struct nd_opt_hdr *search; /* multiple opts */
struct nd_opt_hdr *last; /* multiple opts */
int done;
@@ -344,6 +361,8 @@ union nd_opts {
#define nd_opts_mtu nd_opt_each.mtu
#define nd_opts_adv nd_opt_each.adv
#define nd_opts_hai nd_opt_each.hai
+#define nd_opts_src_addrlist nd_opt_each.src_addrlist
+#define nd_opts_tgt_addrlist nd_opt_each.tgt_addrlist
#define nd_opts_search nd_opt_each.search
#define nd_opts_last nd_opt_each.last
#define nd_opts_done nd_opt_each.done
diff --git a/sys/netinet6/nd6_rtr.c b/sys/netinet6/nd6_rtr.c
index e5e61dd..858b5da 100644
--- a/sys/netinet6/nd6_rtr.c
+++ b/sys/netinet6/nd6_rtr.c
@@ -269,9 +269,6 @@ nd6_ra_input(m, off, icmp6len)
dr0.rtlifetime = ntohs(nd_ra->nd_ra_router_lifetime);
dr0.expire = time_second + dr0.rtlifetime;
dr0.ifp = ifp;
- dr0.advint = 0; /* Mobile IPv6 */
- dr0.advint_expire = 0; /* Mobile IPv6 */
- dr0.advints_lost = 0; /* Mobile IPv6 */
/* unspecified or not? (RFC 2461 6.3.4) */
if (advreachable) {
advreachable = ntohl(advreachable);
diff --git a/sys/netinet6/raw_ip6.c b/sys/netinet6/raw_ip6.c
index a4bd1c3..01f2440 100644
--- a/sys/netinet6/raw_ip6.c
+++ b/sys/netinet6/raw_ip6.c
@@ -331,10 +331,11 @@ rip6_output(m, va_alist)
struct inpcb *in6p;
u_int plen = m->m_pkthdr.len;
int error = 0;
- struct ip6_pktopts opt, *optp = 0;
+ struct ip6_pktopts opt, *stickyopt = NULL;
struct ifnet *oifp = NULL;
int type = 0, code = 0; /* for ICMPv6 output statistics only */
int priv = 0;
+ struct in6_addr *in6a;
va_list ap;
va_start(ap, m);
@@ -344,17 +345,21 @@ rip6_output(m, va_alist)
va_end(ap);
in6p = sotoin6pcb(so);
+ stickyopt = in6p->in6p_outputopts;
priv = 0;
if (so->so_cred->cr_uid == 0)
priv = 1;
dst = &dstsock->sin6_addr;
if (control) {
- if ((error = ip6_setpktoptions(control, &opt, priv, 0)) != 0)
+ if ((error = ip6_setpktoptions(control, &opt,
+ stickyopt, priv, 0,
+ so->so_proto->pr_protocol))
+ != 0) {
goto bad;
- optp = &opt;
- } else
- optp = in6p->in6p_outputopts;
+ }
+ in6p->in6p_outputopts = &opt;
+ }
/*
* For an ICMPv6 packet, we should know its type and code
@@ -393,7 +398,9 @@ rip6_output(m, va_alist)
* XXX Boundary check is assumed to be already done in
* ip6_setpktoptions().
*/
- if (optp && (pi = optp->ip6po_pktinfo) && pi->ipi6_ifindex) {
+ if (in6p->in6p_outputopts &&
+ (pi = in6p->in6p_outputopts->ip6po_pktinfo) &&
+ pi->ipi6_ifindex) {
ip6->ip6_dst.s6_addr16[1] = htons(pi->ipi6_ifindex);
oifp = ifnet_byindex(pi->ipi6_ifindex);
} else if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) &&
@@ -416,19 +423,16 @@ rip6_output(m, va_alist)
/*
* Source address selection.
*/
- {
- struct in6_addr *in6a;
-
- if ((in6a = in6_selectsrc(dstsock, optp, in6p->in6p_moptions,
- &in6p->in6p_route, &in6p->in6p_laddr, &error)) == 0) {
- if (error == 0)
- error = EADDRNOTAVAIL;
- goto bad;
- }
- ip6->ip6_src = *in6a;
- if (in6p->in6p_route.ro_rt)
- oifp = ifnet_byindex(in6p->in6p_route.ro_rt->rt_ifp->if_index);
+ if ((in6a = in6_selectsrc(dstsock, in6p->in6p_outputopts,
+ in6p->in6p_moptions, &in6p->in6p_route, &in6p->in6p_laddr,
+ &error)) == 0) {
+ if (error == 0)
+ error = EADDRNOTAVAIL;
+ goto bad;
}
+ ip6->ip6_src = *in6a;
+ if (in6p->in6p_route.ro_rt)
+ oifp = ifnet_byindex(in6p->in6p_route.ro_rt->rt_ifp->if_index);
ip6->ip6_flow = (ip6->ip6_flow & ~IPV6_FLOWINFO_MASK) |
(in6p->in6p_flowinfo & IPV6_FLOWINFO_MASK);
ip6->ip6_vfc = (ip6->ip6_vfc & ~IPV6_VERSION_MASK) |
@@ -466,7 +470,7 @@ rip6_output(m, va_alist)
*p = in6_cksum(m, ip6->ip6_nxt, sizeof(*ip6), plen);
}
- error = ip6_output(m, optp, &in6p->in6p_route, 0,
+ error = ip6_output(m, in6p->in6p_outputopts, &in6p->in6p_route, 0,
in6p->in6p_moptions, &oifp, in6p);
if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) {
if (oifp)
@@ -482,11 +486,9 @@ rip6_output(m, va_alist)
m_freem(m);
freectl:
- if (optp == &opt && optp->ip6po_rthdr && optp->ip6po_route.ro_rt)
- RTFREE(optp->ip6po_route.ro_rt);
if (control) {
- if (optp == &opt)
- ip6_clearpktopts(optp, 0, -1);
+ ip6_clearpktopts(in6p->in6p_outputopts, -1);
+ in6p->in6p_outputopts = stickyopt;
m_freem(control);
}
return (error);
diff --git a/sys/netinet6/route6.c b/sys/netinet6/route6.c
index 16f790c..c4b0f61 100644
--- a/sys/netinet6/route6.c
+++ b/sys/netinet6/route6.c
@@ -172,8 +172,7 @@ ip6_rthdr0(m, ip6, rh0)
index = addrs - rh0->ip6r0_segleft;
rh0->ip6r0_segleft--;
- /* note that ip6r0_addr does not exist in RFC2292bis */
- nextaddr = rh0->ip6r0_addr + index;
+ nextaddr = ((struct in6_addr *)(rh0 + 1)) + index;
/*
* reject invalid addresses. be proactive about malicious use of
diff --git a/sys/netinet6/udp6_output.c b/sys/netinet6/udp6_output.c
index a685f6a..f753a76 100644
--- a/sys/netinet6/udp6_output.c
+++ b/sys/netinet6/udp6_output.c
@@ -143,7 +143,8 @@ udp6_output(in6p, m, addr6, control, td)
if (td && !suser(td))
priv = 1;
if (control) {
- if ((error = ip6_setpktoptions(control, &opt, priv, 0)) != 0)
+ if ((error = ip6_setpktoptions(control, &opt, stickyopt, priv,
+ 0, IPPROTO_UDP)) != 0)
goto release;
in6p->in6p_outputopts = &opt;
}
@@ -304,7 +305,7 @@ release:
releaseopt:
if (control) {
- ip6_clearpktopts(in6p->in6p_outputopts, 0, -1);
+ ip6_clearpktopts(in6p->in6p_outputopts, -1);
in6p->in6p_outputopts = stickyopt;
m_freem(control);
}
OpenPOWER on IntegriCloud