Page MenuHomeFreeBSD

D16851.id57763.diff
No OneTemporary

D16851.id57763.diff

Index: sys/netinet/dccp.h
===================================================================
--- /dev/null
+++ sys/netinet/dccp.h
@@ -0,0 +1,64 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2019 Tom Jones <thj@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+#ifndef _NETINET_DCCP_H_
+#define _NETINET_DCCP_H_
+
+__FBSDID("$FreeBSD$");
+
+/* DCCP protocol header as per RFC4340 */
+struct dccphdr {
+ uint16_t d_sport;
+ uint16_t d_dport;
+ uint8_t d_doff;
+#if BYTE_ORDER == LITTLE_ENDIAN
+ uint8_t d_cscov:4,
+ d_ccval:4;
+#endif
+#if BYTE_ORDER == BIG_ENDIAN
+ uint8_t d_ccval:4,
+ d_cscov:4;
+#endif
+ uint16_t d_cksum;
+#if BYTE_ORDER == LITTLE_ENDIAN
+ uint8_t d_res:3,
+ d_type:4,
+ d_x:1;
+#endif
+#if BYTE_ORDER == BIG_ENDIAN
+ uint8_t d_x:1,
+ d_type:4,
+ d_res:3;
+#endif
+ uint8_t d_seq[6];
+};
+
+#define DCCP_SHORTHDR 12
+#define DCCP_LONGHDR 16
+#define DCCP_EXTHDR 0x80
+
+#endif /* _NETINET_DCCP_H */
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/netinet/in.h
===================================================================
--- sys/netinet/in.h
+++ sys/netinet/in.h
@@ -169,7 +169,7 @@
#define IPPROTO_BLT 30 /* Bulk Data Transfer */
#define IPPROTO_NSP 31 /* Network Services */
#define IPPROTO_INP 32 /* Merit Internodal */
-#define IPPROTO_SEP 33 /* Sequential Exchange */
+#define IPPROTO_DCCP 33 /* Datagram Congestion Control Protocol */
#define IPPROTO_3PC 34 /* Third Party Connect */
#define IPPROTO_IDPR 35 /* InterDomain Policy Routing */
#define IPPROTO_XTP 36 /* XTP */
Index: sys/netinet6/frag6.c
===================================================================
--- sys/netinet6/frag6.c
+++ sys/netinet6/frag6.c
@@ -67,6 +67,13 @@
#include <netinet/in_systm.h> /* for ECN definitions */
#include <netinet/ip.h> /* for ECN definitions */
+#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>
+
#include <security/mac/mac_framework.h>
/*
@@ -331,6 +338,29 @@
)
break;
+ /*
+ * If first fragment (frag offset is 0) doesn't have a valid upper
+ * layer header drop it per RFC7112.
+ */
+ fragoff = ntohs(ip6f->ip6f_offlg & IP6F_OFF_MASK);
+ if (fragoff == 0 &&
+ (ip6_upperlayerhdr(ip6f->ip6f_nxt) ||
+ V_ip6_allow_unknown_upperlayerhdr)) {
+ if(!ip6_validhdrchain(&m, offset, ip6f->ip6f_nxt, frgpartlen)) {
+ /* remove any fragments that have arrived */
+ if (q6 != head) {
+ IP6STAT_ADD(ip6s_fragdropped, q6->ip6q_nfrag);
+ frag6_freef(q6, hash);
+ }
+ icmp6_error(m, ICMP6_PARAM_PROB,
+ ICMP6_PARAMPROB_FRAGHDRCHAIN, offset);
+ in6_ifstat_inc(dstifp, ifs6_reass_fail);
+ IP6STAT_INC(ip6s_fragdropped);
+ IP6Q_UNLOCK(hash);
+ return (IPPROTO_DONE);
+ }
+ }
+
if (q6 == head) {
/*
* the first fragment to arrive, create a reassembly queue.
@@ -385,7 +415,6 @@
* If it's the 1st fragment, record the length of the
* unfragmentable part and the next header of the fragment header.
*/
- fragoff = ntohs(ip6f->ip6f_offlg & IP6F_OFF_MASK);
if (fragoff == 0) {
q6->ip6q_unfrglen = offset - sizeof(struct ip6_hdr) -
sizeof(struct ip6_frag);
@@ -965,3 +994,94 @@
m->m_flags |= M_FRAGMENTED;
return (0);
}
+
+/*
+ * Ensure that proto is an upper layer header and there is enough space for the
+ * upper layer header.
+ */
+int
+ip6_validhdrchain(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;
+ }
+}
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:
@@ -535,6 +538,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
@@ -644,7 +644,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
@@ -399,6 +399,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
@@ -562,6 +563,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
@@ -544,6 +544,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;
@@ -957,6 +960,12 @@
return;
}
#endif /* IPSEC */
+#ifdef INVARIANTS
+ length = sizeof(struct ip6_hdr) + ntohs(ip6->ip6_plen) - off;
+ if (ip6_upperlayerhdr(nxt))
+ if (!ip6_validhdrchain(&m, off, nxt, length))
+ goto bad;
+#endif
nxt = (*inet6sw[ip6_protox[nxt]].pr_input)(&m, &off, nxt);
}
@@ -1847,6 +1856,29 @@
}
}
+/*
+ * 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;
+ }
+}
+
/*
* System control for IP6
*/
Index: sys/netinet6/ip6_var.h
===================================================================
--- sys/netinet6/ip6_var.h
+++ sys/netinet6/ip6_var.h
@@ -290,6 +290,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)
@@ -297,6 +298,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? */
@@ -410,8 +412,10 @@
struct ip6_pktopts *ip6_copypktopts(struct ip6_pktopts *, int);
int ip6_optlen(struct inpcb *);
int ip6_deletefraghdr(struct mbuf *, int, int);
+int ip6_validhdrchain(struct mbuf **, int, uint8_t, uint16_t);
int ip6_fragment(struct ifnet *, struct mbuf *, int, u_char, int,
uint32_t);
+int ip6_upperlayerhdr(uint8_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
Sat, Nov 16, 1:18 AM (18 h, 51 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
14650452
Default Alt Text
D16851.id57763.diff (12 KB)

Event Timeline