Page MenuHomeFreeBSD

D16851.id64442.diff
No OneTemporary

D16851.id64442.diff

Index: sys/netinet/icmp6.h
===================================================================
--- sys/netinet/icmp6.h
+++ sys/netinet/icmp6.h
@@ -156,6 +156,7 @@
#define ICMP6_PARAMPROB_HEADER 0 /* erroneous header field */
#define ICMP6_PARAMPROB_NEXTHEADER 1 /* unrecognized next header */
#define ICMP6_PARAMPROB_OPTION 2 /* unrecognized option */
+#define ICMP6_PARAMPROB_FRAGHDRCHAIN 3 /* first fragment has incomplete header chain */
#define ICMP6_INFOMSG_MASK 0x80 /* all informational messages */
@@ -587,6 +588,7 @@
uint64_t icp6errs_paramprob_header;
uint64_t icp6errs_paramprob_nextheader;
uint64_t icp6errs_paramprob_option;
+ uint64_t icp6errs_paramprob_fraghdrchain;
uint64_t icp6errs_redirect; /* we regard redirect as an error here */
uint64_t icp6errs_unknown;
};
@@ -626,6 +628,8 @@
#define icp6s_oparamprob_nextheader \
icp6s_outerrhist.icp6errs_paramprob_nextheader
#define icp6s_oparamprob_option icp6s_outerrhist.icp6errs_paramprob_option
+#define icp6s_oparamprob_fraghdrchain \
+ icp6s_outerrhist.icp6errs_paramprob_fraghdrchain
#define icp6s_oredirect icp6s_outerrhist.icp6errs_redirect
#define icp6s_ounknown icp6s_outerrhist.icp6errs_unknown
uint64_t icp6s_pmtuchg; /* path MTU changes */
Index: sys/netinet6/icmp6.c
===================================================================
--- sys/netinet6/icmp6.c
+++ sys/netinet6/icmp6.c
@@ -210,6 +210,9 @@
case ICMP6_PARAMPROB_OPTION:
ICMP6STAT_INC(icp6s_oparamprob_option);
return;
+ case ICMP6_PARAMPROB_FRAGHDRCHAIN:
+ ICMP6STAT_INC(icp6s_oparamprob_fraghdrchain);
+ return;
}
break;
case ND_REDIRECT:
@@ -516,6 +519,7 @@
break;
case ICMP6_PARAMPROB_HEADER:
case ICMP6_PARAMPROB_OPTION:
+ case ICMP6_PARAMPROB_FRAGHDRCHAIN:
code = PRC_PARAMPROB;
break;
default:
Index: sys/netinet6/in6.h
===================================================================
--- sys/netinet6/in6.h
+++ sys/netinet6/in6.h
@@ -641,7 +641,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 */
/*
Index: sys/netinet6/in6_proto.c
===================================================================
--- sys/netinet6/in6_proto.c
+++ sys/netinet6/in6_proto.c
@@ -395,6 +395,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
@@ -538,6 +539,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 "
Index: sys/netinet6/ip6_input.c
===================================================================
--- sys/netinet6/ip6_input.c
+++ sys/netinet6/ip6_input.c
@@ -124,6 +124,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>
+
extern struct domain inet6domain;
u_char ip6_protox[IPPROTO_MAX];
@@ -535,6 +542,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;
@@ -944,6 +954,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 = (*inet6sw[ip6_protox[nxt]].pr_input)(&m, &off, nxt);
}
@@ -1737,6 +1753,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
*/
Index: sys/netinet6/ip6_var.h
===================================================================
--- sys/netinet6/ip6_var.h
+++ 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? */
@@ -390,6 +392,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);
Index: usr.bin/netstat/inet6.c
===================================================================
--- usr.bin/netstat/inet6.c
+++ usr.bin/netstat/inet6.c
@@ -1035,6 +1035,8 @@
"{N:/unrecognized next header}\n");
p_5(icp6s_oparamprob_option, "\t\t{:bad-option/%ju} "
"{N:/unrecognized option}\n");
+ p_5(icp6s_oparamprob_fraghdrchain, "\t\t{:bad-header-chain/%ju} "
+ "{N:/incomplete upper layer first fragment}\n");
p_5(icp6s_oredirect, "\t\t{:redirects/%ju} "
"{N:/redirect}\n");
p_5(icp6s_ounknown, "\t\t{:unknown/%ju} {N:unknown}\n");

File Metadata

Mime Type
text/plain
Expires
Tue, Jan 13, 11:00 PM (10 h, 51 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
27631577
Default Alt Text
D16851.id64442.diff (9 KB)

Event Timeline