Page MenuHomeFreeBSD

D16851.diff
No OneTemporary

D16851.diff

diff --git a/sys/netinet6/in6.h b/sys/netinet6/in6.h
--- a/sys/netinet6/in6.h
+++ b/sys/netinet6/in6.h
@@ -646,7 +646,8 @@
* queue */
#define IPV6CTL_MAXFRAGSPERPACKET 53 /* Max fragments per packet */
#define IPV6CTL_MAXFRAGBUCKETSIZE 54 /* Max reassembly queues per bucket */
-#define IPV6CTL_MAXID 55
+#define IPV6CTL_UNKNOWN_UPPERLAYERHDR 55 /* Allow unknown upper layer headers */
+#define IPV6CTL_MAXID 56
#endif /* __BSD_VISIBLE */
/*
diff --git a/sys/netinet6/in6_proto.c b/sys/netinet6/in6_proto.c
--- a/sys/netinet6/in6_proto.c
+++ b/sys/netinet6/in6_proto.c
@@ -216,6 +216,7 @@
* walk list every 5 sec. */
VNET_DEFINE(int, ip6_mcast_pmtu) = 0; /* enable pMTU discovery for multicast? */
VNET_DEFINE(int, ip6_v6only) = 1;
+VNET_DEFINE(int, ip6_allow_unknown_upperlayerhdr) = 1;
VNET_DEFINE(time_t, ip6_log_time) = (time_t)0L;
#ifdef IPSTEALTH
@@ -365,6 +366,9 @@
SYSCTL_INT(_net_inet6_ip6, IPV6CTL_V6ONLY, v6only,
CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(ip6_v6only), 0,
"Restrict AF_INET6 sockets to IPv6 addresses only");
+SYSCTL_INT(_net_inet6_ip6, IPV6CTL_UNKNOWN_UPPERLAYERHDR, allow_unknown_upperlayerhdr,
+ CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(ip6_allow_unknown_upperlayerhdr), 0,
+ "Allow unknown upper layer protocols to skip header chain validation");
SYSCTL_INT(_net_inet6_ip6, IPV6CTL_AUTO_LINKLOCAL, auto_linklocal,
CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(ip6_auto_linklocal), 0,
"Default value of per-interface flag for automatically adding an IPv6 "
diff --git a/sys/netinet6/ip6_input.c b/sys/netinet6/ip6_input.c
--- a/sys/netinet6/ip6_input.c
+++ b/sys/netinet6/ip6_input.c
@@ -131,6 +131,13 @@
#include <netinet6/ip6protosw.h>
+#include <netinet/ip_var.h>
+#include <netinet/tcp.h>
+#include <netinet/sctp.h>
+#include <netinet/udp.h>
+#include <netinet/dccp.h>
+#include <net/if_gre.h>
+
ipproto_input_t *ip6_protox[IPPROTO_MAX] = {
[0 ... IPPROTO_MAX - 1] = rip6_input };
ipproto_ctlinput_t *ip6_ctlprotox[IPPROTO_MAX] = {
@@ -526,6 +533,9 @@
struct in6_ifaddr *ia;
struct ifnet *rcvif;
u_int32_t plen;
+#ifdef INVARIANTS
+ u_int16_t length;
+#endif
u_int32_t rtalert = ~0;
int off = sizeof(struct ip6_hdr), nest;
int nxt, ours = 0;
@@ -919,6 +929,12 @@
return;
}
#endif /* IPSEC */
+#ifdef INVARIANTS
+ length = sizeof(struct ip6_hdr) + ntohs(ip6->ip6_plen) - off;
+ if (ip6_upperlayerhdr(nxt))
+ if (!ip6_valid_hdrchain(&m, off, nxt, length))
+ goto bad;
+#endif
nxt = ip6_protox[nxt](&m, &off, nxt);
}
@@ -1715,6 +1731,172 @@
}
}
+/*
+ * Check if proto is a supported upper layer protocol.
+ */
+int
+ip6_upperlayerhdr(uint8_t proto)
+{
+ switch (proto) {
+ case IPPROTO_ICMPV6:
+ case IPPROTO_IPV6:
+ case IPPROTO_ESP:
+ case IPPROTO_SCTP:
+ case IPPROTO_TCP:
+ case IPPROTO_UDP:
+ case IPPROTO_UDPLITE:
+ case IPPROTO_DCCP:
+ case IPPROTO_IP:
+ case IPPROTO_GRE:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+/*
+ * Search the header chain for the first upper layer protocol and return the
+ * offset of that header. m will be kept untainted.
+ */
+int
+ip6_first_upperlayerhdr(const struct mbuf *m, int off, int proto, int *nxtp)
+{
+ int newoff;
+ int nxt;
+ int nest = 0;
+
+ if (!nxtp) {
+ nxt = -1;
+ nxtp = &nxt;
+ }
+ while (nest++ < V_ip6_hdrnestlimit) {
+ if (ip6_upperlayerhdr(proto) == 1)
+ return off;
+ newoff = ip6_nexthdr(m, off, proto, nxtp);
+ if (newoff < 0)
+ return -1;
+ else if (newoff < off)
+ return -1; /* invalid */
+ else if (newoff == off)
+ return -1;
+
+ off = newoff;
+ proto = *nxtp;
+ }
+ return -1;
+}
+
+/*
+ * Ensure that proto is an upper layer header and there is enough space for the
+ * upper layer header.
+ */
+int
+ip6_valid_upperlayerhdr(struct mbuf **mp, int offset, uint8_t proto, uint16_t length)
+{
+ struct mbuf *m;
+ struct ipv6_hdr *ip6;
+ struct ip *ip4;
+ struct tcphdr *tp;
+ struct dccphdr *dccp;
+ uint16_t hdrlen;
+
+ m = *mp;
+ ip6 = mtod(m, struct ipv6_hdr *);
+
+ switch (proto) {
+ case IPPROTO_ICMPV6:
+ if (length < sizeof(struct icmp6_hdr))
+ return 0;
+ return 1;
+ case IPPROTO_IPV6:
+ if (length < sizeof(struct ip6_hdr))
+ return 0;
+ return 1;
+ case IPPROTO_ESP:
+ if (length < sizeof(uint32_t))
+ return 0;
+ return 1;
+ case IPPROTO_SCTP:
+ if (length < sizeof(struct sctphdr))
+ return 0;
+ return 1;
+ case IPPROTO_TCP:
+ if (length < sizeof(struct tcphdr))
+ return 0;
+ /* Get the tcp header in the first mbuf. */
+ if (m->m_len < offset + sizeof(struct tcphdr))
+ if ((m = m_pullup(m, offset + sizeof(struct tcphdr))) == NULL) {
+ *mp = NULL;
+ return 0;
+ }
+ tp = (struct tcphdr *)((caddr_t)ip6 + offset);
+ hdrlen = tp->th_off << 2;
+ if (length < hdrlen)
+ return 0;
+ return 1;
+ case IPPROTO_UDP:
+ case IPPROTO_UDPLITE:
+ if (length < sizeof(struct udphdr))
+ return 0;
+ return 1;
+ case IPPROTO_DCCP:
+ if (length < DCCP_SHORTHDR)
+ return 0;
+ /* Get the dccp short header in the first mbuf. */
+ if (m->m_len < offset + DCCP_SHORTHDR)
+ if ((m = m_pullup(m, offset + DCCP_SHORTHDR)) == NULL) {
+ *mp = NULL;
+ return 0;
+ }
+ dccp = (struct dccphdr *)((caddr_t)ip6 + offset);
+ hdrlen = dccp->d_x;
+ if (hdrlen & DCCP_EXTHDR && length < DCCP_LONGHDR)
+ return 0;
+ return 1;
+ case IPPROTO_IP:
+ if (length < sizeof(struct ip))
+ return 0;
+ /* Get the ip header in the first mbuf. */
+ if (m->m_len < offset + sizeof(struct ip))
+ if ((m = m_pullup(m, offset + sizeof(struct ip))) == NULL) {
+ *mp = NULL;
+ return 0;
+ }
+ ip4 = (struct ip *)((caddr_t)ip6 + offset);
+ hdrlen = ip4->ip_hl << 2;
+ if (length < hdrlen)
+ return 0;
+ return 1;
+ case IPPROTO_GRE:
+ if (length < sizeof(struct grehdr))
+ return 0;
+ return 1;
+ default:
+ /* not a known upper layer protocol */
+ return 0;
+ }
+}
+
+uint8_t
+ip6_valid_hdrchain(struct mbuf **mp, int offset, uint8_t proto, uint16_t length)
+{
+ int nxtp = proto;
+ struct mbuf *m;
+ m = *mp;
+
+ offset = ip6_first_upperlayerhdr(m, offset, proto, &nxtp);
+
+ if (offset == -1) {
+ if (V_ip6_allow_unknown_upperlayerhdr)
+ return 1;
+
+ else
+ return 0;
+ }
+
+ return ip6_valid_upperlayerhdr(mp, offset, proto, length - offset);
+}
+
/*
* System control for IP6
*/
diff --git a/sys/netinet6/ip6_var.h b/sys/netinet6/ip6_var.h
--- a/sys/netinet6/ip6_var.h
+++ b/sys/netinet6/ip6_var.h
@@ -277,6 +277,7 @@
* walk list every 5 sec. */
VNET_DECLARE(int, ip6_mcast_pmtu); /* enable pMTU discovery for multicast? */
VNET_DECLARE(int, ip6_v6only);
+VNET_DECLARE(int, ip6_allow_unknown_upperlayerhdr);
#define V_ip6_defhlim VNET(ip6_defhlim)
#define V_ip6_defmcasthlim VNET(ip6_defmcasthlim)
#define V_ip6_forwarding VNET(ip6_forwarding)
@@ -284,6 +285,7 @@
#define V_ip6_rr_prune VNET(ip6_rr_prune)
#define V_ip6_mcast_pmtu VNET(ip6_mcast_pmtu)
#define V_ip6_v6only VNET(ip6_v6only)
+#define V_ip6_allow_unknown_upperlayerhdr VNET(ip6_allow_unknown_upperlayerhdr)
VNET_DECLARE(struct socket *, ip6_mrouter); /* multicast routing daemon */
VNET_DECLARE(int, ip6_sendredirects); /* send IP redirects when forwarding? */
@@ -385,6 +387,10 @@
int ip6_deletefraghdr(struct mbuf *, int, int);
int ip6_fragment(struct ifnet *, struct mbuf *, int, u_char, int,
uint32_t);
+int ip6_upperlayerhdr(uint8_t);
+int ip6_first_upperlayerhdr(const struct mbuf *, int , int , int *);
+int ip6_valid_upperlayerhdr(struct mbuf **, int , uint8_t , uint16_t);
+uint8_t ip6_valid_hdrchain(struct mbuf **, int , uint8_t , uint16_t);
int route6_input(struct mbuf **, int *, int);

File Metadata

Mime Type
text/plain
Expires
Fri, Nov 15, 1:48 AM (4 h, 9 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
14636630
Default Alt Text
D16851.diff (7 KB)

Event Timeline