diff --git include/net/inet_sock.h include/net/inet_sock.h index 1833c3f..c389fa5 100644 --- include/net/inet_sock.h +++ include/net/inet_sock.h @@ -177,6 +177,7 @@ struct inet_sock { __be32 mc_addr; struct ip_mc_socklist __rcu *mc_list; struct inet_cork_full cork; + bool evil; }; #define IPCORK_OPT 1 /* ip-options has been held in ipcork.opt */ diff --git include/net/ip.h include/net/ip.h index 23be0fd..aa8b3f6 100644 --- include/net/ip.h +++ include/net/ip.h @@ -78,6 +78,7 @@ struct ip_ra_chain { extern struct ip_ra_chain __rcu *ip_ra_chain; /* IP flags. */ +#define IP_EVIL 0x8000 /* Flag: "The Evil Bit" */ #define IP_CE 0x8000 /* Flag: "Congestion" */ #define IP_DF 0x4000 /* Flag: "Don't Fragment" */ #define IP_MF 0x2000 /* Flag: "More Fragments" */ @@ -229,9 +230,12 @@ extern int inet_peer_maxttl; /* From ip_input.c */ extern int sysctl_ip_early_demux; +extern int sysctl_ip_hear_no_evil; /* From ip_output.c */ extern int sysctl_ip_dynaddr; +extern int sysctl_ip_do_rfc3514; +extern int sysctl_ip_speak_no_evil; void ipfrag_init(void); diff --git include/uapi/linux/in.h include/uapi/linux/in.h index 393c5de..3e34975 100644 --- include/uapi/linux/in.h +++ include/uapi/linux/in.h @@ -148,6 +148,9 @@ struct in_addr { #define IP_DEFAULT_MULTICAST_TTL 1 #define IP_DEFAULT_MULTICAST_LOOP 1 +/* RFC3514: The security flag in the IPv4 header */ +#define IP_EVIL_INTENT 666 + /* Request struct for multicast socket ops */ struct ip_mreq { diff --git net/ipv4/ip_input.c net/ipv4/ip_input.c index 3d4da2c..f9303f2 100644 --- net/ipv4/ip_input.c +++ net/ipv4/ip_input.c @@ -147,6 +147,9 @@ #include #include +int sysctl_ip_hear_no_evil __read_mostly; +EXPORT_SYMBOL(sysctl_ip_hear_no_evil); + /* * Process Router Attention IP option (RFC 2113) */ @@ -398,6 +401,15 @@ int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, iph = ip_hdr(skb); /* + * Check for RFC3514 (EVIL) packets. + */ + if (sysctl_ip_hear_no_evil && + ip_hdr(skb)->frag_off & htons(IP_EVIL)) { + IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_INDISCARDS); + goto drop; + } + + /* * RFC1122: 3.2.1.2 MUST silently discard any IP frame that fails the checksum. * * Is the datagram acceptable? diff --git net/ipv4/ip_output.c net/ipv4/ip_output.c index 73c6b63..a2f85d1 100644 --- net/ipv4/ip_output.c +++ net/ipv4/ip_output.c @@ -82,6 +82,10 @@ int sysctl_ip_default_ttl __read_mostly = IPDEFTTL; EXPORT_SYMBOL(sysctl_ip_default_ttl); +int sysctl_ip_do_rfc3514 __read_mostly; +EXPORT_SYMBOL(sysctl_ip_do_rfc3514); +int sysctl_ip_speak_no_evil __read_mostly; +EXPORT_SYMBOL(sysctl_ip_speak_no_evil); /* Generate a checksum for an outgoing IP datagram. */ void ip_send_check(struct iphdr *iph) @@ -95,6 +99,9 @@ int __ip_local_out(struct sk_buff *skb) { struct iphdr *iph = ip_hdr(skb); + if (sysctl_ip_do_rfc3514 && skb->sk && inet_sk(skb->sk)->evil) + iph->frag_off |= htons(IP_EVIL); + iph->tot_len = htons(skb->len); ip_send_check(iph); return nf_hook(NFPROTO_IPV4, NF_INET_LOCAL_OUT, skb, NULL, @@ -172,6 +179,14 @@ static inline int ip_finish_output2(struct sk_buff *skb) struct neighbour *neigh; u32 nexthop; + /* RFC3514 */ + if (sysctl_ip_speak_no_evil && + ip_hdr(skb)->frag_off & htons(IP_EVIL)) { + IP_INC_STATS(dev_net(dev), IPSTATS_MIB_OUTDISCARDS); + kfree_skb(skb); + return -EACCES; + } + if (rt->rt_type == RTN_MULTICAST) { IP_UPD_PO_STATS(dev_net(dev), IPSTATS_MIB_OUTMCAST, skb->len); } else if (rt->rt_type == RTN_BROADCAST) diff --git net/ipv4/ip_sockglue.c net/ipv4/ip_sockglue.c index 580dd96..2bc450b 100644 --- net/ipv4/ip_sockglue.c +++ net/ipv4/ip_sockglue.c @@ -496,6 +496,7 @@ static int do_ip_setsockopt(struct sock *sk, int level, case IP_MULTICAST_ALL: case IP_MULTICAST_LOOP: case IP_RECVORIGDSTADDR: + case IP_EVIL_INTENT: if (optlen >= sizeof(int)) { if (get_user(val, (int __user *) optval)) return -EFAULT; @@ -1030,6 +1031,16 @@ mc_msf_out: inet->min_ttl = val; break; + case IP_EVIL_INTENT: + if (!sysctl_ip_do_rfc3514) + goto e_inval; + if (optlen < 1) + goto e_inval; + if (val != 0 && val != 1) + goto e_inval; + inet->evil = val; + break; + default: err = -ENOPROTOOPT; break; @@ -1334,6 +1345,9 @@ static int do_ip_getsockopt(struct sock *sk, int level, int optname, case IP_MINTTL: val = inet->min_ttl; break; + case IP_EVIL_INTENT: + val = inet->evil; + break; default: release_sock(sk); return -ENOPROTOOPT; diff --git net/ipv4/sysctl_net_ipv4.c net/ipv4/sysctl_net_ipv4.c index 44eba05..40b176f 100644 --- net/ipv4/sysctl_net_ipv4.c +++ net/ipv4/sysctl_net_ipv4.c @@ -757,6 +757,33 @@ static struct ctl_table ipv4_table[] = { .proc_handler = proc_dointvec_minmax, .extra1 = &one }, + { + .procname = "rfc3514", + .data = &sysctl_ip_do_rfc3514, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec_minmax, + .extra1 = &zero, + .extra2 = &one + }, + { + .procname = "ip_speak_no_evil", + .data = &sysctl_ip_speak_no_evil, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec_minmax, + .extra1 = &zero, + .extra2 = &one + }, + { + .procname = "ip_hear_no_evil", + .data = &sysctl_ip_hear_no_evil, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec_minmax, + .extra1 = &zero, + .extra2 = &one + }, { } };