From 09139a45372f3b36cb9f49c8f3de00659037e3ca Mon Sep 17 00:00:00 2001 From: "Matthew N. Dodd" Date: Tue, 1 Apr 2003 08:21:44 +0000 Subject: [PATCH] Implement support for RFC 3514 (The Security Flag in the IPv4 Header). (See: ftp://ftp.rfc-editor.org/in-notes/rfc3514.txt) This fulfills the host requirements for userland support by way of the setsockopt() IP_EVIL_INTENT message. There are three sysctl tunables provided to govern system behavior. net.inet.ip.rfc3514: Enables support for rfc3514. As this is an Informational RFC and support is not yet widespread this option is disabled by default. net.inet.ip.hear_no_evil If set the host will discard all received evil packets. net.inet.ip.speak_no_evil If set the host will discard all transmitted evil packets. The IP statistics counter 'ips_evil' (available via 'netstat') provides information on the number of 'evil' packets recieved. For reference, the '-E' option to 'ping' has been provided to demonstrate and test the implementation. --- sbin/ping/ping.8 | 6 ++++-- sbin/ping/ping.c | 14 +++++++++++++- share/man/man4/inet.4 | 11 +++++++++++ share/man/man4/ip.4 | 9 +++++++++ sys/netinet/in.h | 2 ++ sys/netinet/in_pcb.h | 1 + sys/netinet/ip.h | 1 + sys/netinet/ip_input.c | 14 ++++++++++++++ sys/netinet/ip_output.c | 29 ++++++++++++++++++++++++++++- sys/netinet/ip_var.h | 1 + usr.bin/netstat/inet.c | 1 + 11 files changed, 85 insertions(+), 4 deletions(-) diff --git a/sbin/ping/ping.8 b/sbin/ping/ping.8 index 71fa31a8cec9..49223e4800cc 100644 --- a/sbin/ping/ping.8 +++ b/sbin/ping/ping.8 @@ -42,7 +42,7 @@ packets to network hosts .Sh SYNOPSIS .Nm -.Op Fl AaDdfnoQqRrv +.Op Fl AaDdEfnoQqRrv .Op Fl c Ar count .Op Fl i Ar wait .Op Fl l Ar preload @@ -56,7 +56,7 @@ packets to network hosts .Op Fl z Ar tos .Ar host .Nm -.Op Fl AaDdfLnoQqRrv +.Op Fl AaDdEfLnoQqRrv .Op Fl c Ar count .Op Fl I Ar iface .Op Fl i Ar wait @@ -122,6 +122,8 @@ If this option is not specified, will operate until interrupted. .It Fl D Set the Don't Fragment bit. +.It Fl E +Set the EVIL bit. .It Fl d Set the .Dv SO_DEBUG diff --git a/sbin/ping/ping.c b/sbin/ping/ping.c index 0d41c92d91a5..01a14596fb6b 100644 --- a/sbin/ping/ping.c +++ b/sbin/ping/ping.c @@ -143,6 +143,7 @@ int options; #define F_HDRINCL 0x40000 #define F_MASK 0x80000 #define F_TIME 0x100000 +#define F_SO_EVIL 0x200000 /* * MAX_DUP_CHK is the number of bits in received table, i.e. the maximum @@ -256,7 +257,7 @@ main(argc, argv) outpack = outpackhdr + sizeof(struct ip); while ((ch = getopt(argc, argv, - "Aac:DdfI:i:Ll:M:m:nop:QqRrS:s:T:t:vz:" + "Aac:DdEfI:i:Ll:M:m:nop:QqRrS:s:T:t:vz:" #ifdef IPSEC #ifdef IPSEC_POLICY_IPSEC "P:" @@ -286,6 +287,9 @@ main(argc, argv) case 'd': options |= F_SO_DEBUG; break; + case 'E': + options |= F_SO_EVIL; + break; case 'f': if (uid) { errno = EPERM; @@ -547,6 +551,10 @@ main(argc, argv) if (options & F_SO_DONTROUTE) (void)setsockopt(s, SOL_SOCKET, SO_DONTROUTE, (char *)&hold, sizeof(hold)); + if (options & F_SO_EVIL) + if (setsockopt(s, IPPROTO_IP, IP_EVIL_INTENT, (char *)&hold, + sizeof(hold)) != 0) + err(EX_OSERR, "setsockopt(s, IPPROTO_IP, IP_EVIL_INTENT, ...)"); #ifdef IPSEC #ifdef IPSEC_POLICY_IPSEC if (options & F_POLICY) { @@ -593,6 +601,8 @@ main(argc, argv) ip->ip_tos = tos; ip->ip_id = 0; ip->ip_off = df ? IP_DF : 0; + if (options & F_SO_EVIL) + ip->ip_off |= IP_EVIL; ip->ip_ttl = ttl; ip->ip_p = IPPROTO_ICMP; ip->ip_src.s_addr = source ? sock_in.sin_addr.s_addr : INADDR_ANY; @@ -991,6 +1001,8 @@ pr_pack(buf, cc, from, tv) (void)printf(" ttl=%d", ip->ip_ttl); if (timing) (void)printf(" time=%.3f ms", triptime); + if (ip->ip_off & IP_EVIL) + (void)printf(" (EVIL)"); if (dupflag) (void)printf(" (DUP!)"); if (options & F_AUDIBLE) diff --git a/share/man/man4/inet.4 b/share/man/man4/inet.4 index d15c204dc545..3cf03f8f15a4 100644 --- a/share/man/man4/inet.4 +++ b/share/man/man4/inet.4 @@ -261,6 +261,17 @@ adaptation described above. .Pq ip.rtmaxcache Integer: trigger level of cached, unreferenced, protocol-cloned routes which initiates dynamic adaptation (default 128). +.It Dv IPCTL_RFC3514 +.Pq ip.rfc3514 +Boolean: Enable support for RFC3514. Defaults to off. +.It Dv IPCTL_SPEAK_NO_EVIL +.Pq ip.speak_no_evil +Boolean: Prevent the transmission of RFC3514 (EVIL) packets. +Defaults to off. +.It Dv IPCTL_HEAR_NO_EVIL +.Pq ip.hear_no_evil +Boolean: Prevent the reception of RFC3514 (EVIL) packets. +Defaults to off. .El .Sh SEE ALSO .Xr ioctl 2 , diff --git a/share/man/man4/ip.4 b/share/man/man4/ip.4 index 713001469614..48962147c51d 100644 --- a/share/man/man4/ip.4 +++ b/share/man/man4/ip.4 @@ -164,6 +164,15 @@ control message from can be used directly as a control message for .Xr sendmsg 2 . .Pp +.Dv IP_EVIL_INTENT can be used to specify that IP packets should have their +EVIL option set as per RFC3514. +The cmsghdr fields should have the following values: +.Bd -literal +cmsg_len = sizeof(struct in_addr) +cmsg_level = IPPROTO_IP +cmsg_type = IP_EVIL_INTENT +.Ed +.Pp .Dv IP_PORTRANGE may be used to set the port range used for selecting a local port number on a socket with an unspecified (zero) port number. diff --git a/sys/netinet/in.h b/sys/netinet/in.h index 83eeae78b9d7..49f83e5b82ad 100644 --- a/sys/netinet/in.h +++ b/sys/netinet/in.h @@ -399,6 +399,8 @@ __END_DECLS #define IP_DUMMYNET_FLUSH 62 /* flush dummynet */ #define IP_DUMMYNET_GET 64 /* get entire dummynet pipes */ +#define IP_EVIL_INTENT 65 /* RFC3514 */ + /* * Defaults and limits for options */ diff --git a/sys/netinet/in_pcb.h b/sys/netinet/in_pcb.h index efb7862a5407..eb02c6e2e71c 100644 --- a/sys/netinet/in_pcb.h +++ b/sys/netinet/in_pcb.h @@ -276,6 +276,7 @@ struct inpcbinfo { /* XXX documentation, prefixes */ #define INP_RECVIF 0x80 /* receive incoming interface */ #define INP_MTUDISC 0x100 /* user can do MTU discovery */ #define INP_FAITH 0x200 /* accept FAITH'ed connections */ +#define INP_EVIL 0x400 /* Packet has evil intentions */ #define IN6P_IPV6_V6ONLY 0x008000 /* restrict AF_INET6 socket for v6 */ diff --git a/sys/netinet/ip.h b/sys/netinet/ip.h index 24f66ceb24db..4b3c0976eb32 100644 --- a/sys/netinet/ip.h +++ b/sys/netinet/ip.h @@ -62,6 +62,7 @@ struct ip { u_short ip_id; /* identification */ u_short ip_off; /* fragment offset field */ #define IP_RF 0x8000 /* reserved fragment flag */ +#define IP_EVIL 0x8000 /* packet is evil */ #define IP_DF 0x4000 /* dont fragment flag */ #define IP_MF 0x2000 /* more fragments flag */ #define IP_OFFMASK 0x1fff /* mask for fragmenting bits */ diff --git a/sys/netinet/ip_input.c b/sys/netinet/ip_input.c index e26cc8dd55fe..8b130f5d829c 100644 --- a/sys/netinet/ip_input.c +++ b/sys/netinet/ip_input.c @@ -134,6 +134,11 @@ SYSCTL_INT(_net_inet_ip, OID_AUTO, sendsourcequench, CTLFLAG_RW, &ip_sendsourcequench, 0, "Enable the transmission of source quench packets"); +static int hear_no_evil = 0; +SYSCTL_INT(_net_inet_ip, OID_AUTO, hear_no_evil, CTLFLAG_RW, + &hear_no_evil, 0, + "Drop all received EVIL packets."); + /* * XXX - Setting ip_checkinterface mostly implements the receive side of * the Strong ES model described in RFC 1122, but since the routing table @@ -406,6 +411,15 @@ ip_input(struct mbuf *m) } ip->ip_off = ntohs(ip->ip_off); + /* + * Check for RFC3514 (EVIL) packets. + */ + if (ip->ip_off & IP_EVIL) { + ipstat.ips_evil++; + if (hear_no_evil) + goto bad; + } + /* * Check that the amount of data in the buffers * is as at least much as the IP header would have us expect. diff --git a/sys/netinet/ip_output.c b/sys/netinet/ip_output.c index c00ac4cd512e..822ca8993ce4 100644 --- a/sys/netinet/ip_output.c +++ b/sys/netinet/ip_output.c @@ -101,6 +101,13 @@ int mbuf_frag_size = 0; SYSCTL_INT(_net_inet_ip, OID_AUTO, mbuf_frag_size, CTLFLAG_RW, &mbuf_frag_size, 0, "Fragment outgoing mbufs to this size"); #endif +static int ip_do_rfc3514 = 0; +SYSCTL_INT(_net_inet_ip, OID_AUTO, rfc3514, CTLFLAG_RW, + &ip_do_rfc3514, 0, "IPv4 Header Security Flag Support"); + +static int speak_no_evil = 0; +SYSCTL_INT(_net_inet_ip, OID_AUTO, speak_no_evil, CTLFLAG_RW, + &speak_no_evil, 0, "Drop all EVIL packets before output."); static struct mbuf *ip_insertoptions(struct mbuf *, struct mbuf *, int *); static struct ifnet *ip_multicast_if(struct in_addr *, int *); @@ -228,7 +235,7 @@ ip_output(m0, opt, ro, flags, imo, inp) if ((flags & (IP_FORWARDING|IP_RAWOUTPUT)) == 0) { ip->ip_v = IPVERSION; ip->ip_hl = hlen >> 2; - ip->ip_off &= IP_DF; + ip->ip_off &= IP_DF|IP_EVIL; #ifdef RANDOM_IP_ID ip->ip_id = ip_randomid(); #else @@ -239,6 +246,17 @@ ip_output(m0, opt, ro, flags, imo, inp) hlen = ip->ip_hl << 2; } + /* RFC3514 */ + if ((inp != NULL) && /* Originated */ + ip_do_rfc3514 && /* Supported */ + ((inp->inp_flags & INP_EVIL) == INP_EVIL)) /* Optioned */ + ip->ip_off |= IP_EVIL; + + if (speak_no_evil && (ip->ip_off & IP_EVIL)) { + error = EACCES; + goto bad; + } + #ifdef FAST_IPSEC if (ro == NULL) { ro = &iproute; @@ -1426,6 +1444,7 @@ ip_ctloutput(so, sopt) case IP_RECVDSTADDR: case IP_RECVIF: case IP_FAITH: + case IP_EVIL_INTENT: error = sooptcopyin(sopt, &optval, sizeof optval, sizeof optval); if (error) @@ -1464,6 +1483,12 @@ ip_ctloutput(so, sopt) case IP_FAITH: OPTSET(INP_FAITH); break; + case IP_EVIL_INTENT: + if (ip_do_rfc3514) { + OPTSET(INP_EVIL); + } else + error = EINVAL; + break; } break; #undef OPTSET @@ -1596,6 +1621,8 @@ ip_ctloutput(so, sopt) case IP_FAITH: optval = OPTBIT(INP_FAITH); break; + case IP_EVIL: + optval = OPTBIT(INP_EVIL); } error = sooptcopyout(sopt, &optval, sizeof optval); break; diff --git a/sys/netinet/ip_var.h b/sys/netinet/ip_var.h index c8df2e673e4a..4e9907b12b2d 100644 --- a/sys/netinet/ip_var.h +++ b/sys/netinet/ip_var.h @@ -132,6 +132,7 @@ struct ipstat { u_long ips_notmember; /* multicasts for unregistered grps */ u_long ips_nogif; /* no match gif found */ u_long ips_badaddr; /* invalid address on header */ + u_long ips_evil; /* EVIL packets received */ }; #ifdef _KERNEL diff --git a/usr.bin/netstat/inet.c b/usr.bin/netstat/inet.c index d608e1a3ba02..3aa486e3bb5c 100644 --- a/usr.bin/netstat/inet.c +++ b/usr.bin/netstat/inet.c @@ -568,6 +568,7 @@ ip_stats(u_long off __unused, const char *name, int af1 __unused) p(ips_cantfrag, "\t%lu datagram%s that can't be fragmented\n"); p(ips_nogif, "\t%lu tunneling packet%s that can't find gif\n"); p(ips_badaddr, "\t%lu datagram%s with bad address in header\n"); + p(ips_evil, "\t%lu EVIL datagram%s received.\n"); #undef p #undef p1a }