IPv6: Generic TTL Security Mechanism (final version)
authorStephen Hemminger <shemminger@vyatta.com>
Thu, 22 Apr 2010 22:24:53 +0000 (15:24 -0700)
committerDavid S. Miller <davem@davemloft.net>
Thu, 22 Apr 2010 22:24:53 +0000 (15:24 -0700)
This patch adds IPv6 support for RFC5082 Generalized TTL Security Mechanism.

Not to users of mapped address; the IPV6 and IPV4 socket options are seperate.
The server does have to deal with both IPv4 and IPv6 socket options
and the client has to handle the different for each family.

On client:
int ttl = 255;
getaddrinfo(argv[1], argv[2], &hint, &result);

for (rp = result; rp != NULL; rp = rp->ai_next) {
s = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if (s < 0) continue;

if (rp->ai_family == AF_INET) {
setsockopt(s, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl));
} else if (rp->ai_family == AF_INET6) {
setsockopt(s, IPPROTO_IPV6,  IPV6_UNICAST_HOPS,
&ttl, sizeof(ttl)))
}

if (connect(s, rp->ai_addr, rp->ai_addrlen) == 0) {
   ...

On server:
int minttl = 255 - maxhops;

getaddrinfo(NULL, port, &hints, &result);
for (rp = result; rp != NULL; rp = rp->ai_next) {
s = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if (s < 0) continue;

if (rp->ai_family == AF_INET6)
setsockopt(s, IPPROTO_IPV6,  IPV6_MINHOPCOUNT,
&minttl, sizeof(minttl));
setsockopt(s, IPPROTO_IP, IP_MINTTL, &minttl, sizeof(minttl));

if (bind(s, rp->ai_addr, rp->ai_addrlen) == 0)
break
...

Signed-off-by: Stephen Hemminger <shemminger@vyatta.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/linux/in6.h
include/linux/ipv6.h
net/ipv6/ipv6_sockglue.c
net/ipv6/tcp_ipv6.c

index bd55c6e46b2ea6bbe72f582eac8cda8c14c0bd60..9b90cb296eb1b52008a4cff83b163e0f47accd5e 100644 (file)
@@ -265,6 +265,9 @@ struct in6_flowlabel_req {
 #define IPV6_PREFER_SRC_CGA            0x0008
 #define IPV6_PREFER_SRC_NONCGA         0x0800
 
+/* RFC5082: Generalized Ttl Security Mechanism */
+#define IPV6_MINHOPCOUNT               73
+
 /*
  * Multicast Routing:
  * see include/linux/mroute6.h.
index e0cc9a7db2b5fdc689bceb7d2a3fb5d00aea6ede..1bdbebf08d16221d7b341c90e4fd70b3a6665c2d 100644 (file)
@@ -348,6 +348,7 @@ struct ipv6_pinfo {
                                                 * 010: prefer public address
                                                 * 100: prefer care-of address
                                                 */
+       __u8                    min_hopcount;
        __u8                    tclass;
 
        __u32                   dst_cookie;
index 1160400e9dbdf76a7df77e4acb0f878a8e14b982..92295ad3487ad441bbf65c1787f4b0fc142eb31f 100644 (file)
@@ -767,6 +767,14 @@ pref_skip_coa:
 
                break;
            }
+       case IPV6_MINHOPCOUNT:
+               if (optlen < sizeof(int))
+                       goto e_inval;
+               if (val < 0 || val > 255)
+                       goto e_inval;
+               np->min_hopcount = val;
+               retv = 0;
+               break;
        }
 
        release_sock(sk);
@@ -1116,6 +1124,10 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
                        val |= IPV6_PREFER_SRC_HOME;
                break;
 
+       case IPV6_MINHOPCOUNT:
+               val = np->min_hopcount;
+               break;
+
        default:
                return -ENOPROTOOPT;
        }
index 1ababbb41131309441d925955c81e02adc1f192e..6603511e367305477f9cdd3c29250c11d5f6a0ef 100644 (file)
@@ -353,6 +353,11 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
        if (sk->sk_state == TCP_CLOSE)
                goto out;
 
+       if (ipv6_hdr(skb)->hop_limit < inet6_sk(sk)->min_hopcount) {
+               NET_INC_STATS_BH(net, LINUX_MIB_TCPMINTTLDROP);
+               goto out;
+       }
+
        tp = tcp_sk(sk);
        seq = ntohl(th->seq);
        if (sk->sk_state != TCP_LISTEN &&
@@ -1678,6 +1683,7 @@ ipv6_pktoptions:
 static int tcp_v6_rcv(struct sk_buff *skb)
 {
        struct tcphdr *th;
+       struct ipv6hdr *hdr;
        struct sock *sk;
        int ret;
        struct net *net = dev_net(skb->dev);
@@ -1704,12 +1710,13 @@ static int tcp_v6_rcv(struct sk_buff *skb)
                goto bad_packet;
 
        th = tcp_hdr(skb);
+       hdr = ipv6_hdr(skb);
        TCP_SKB_CB(skb)->seq = ntohl(th->seq);
        TCP_SKB_CB(skb)->end_seq = (TCP_SKB_CB(skb)->seq + th->syn + th->fin +
                                    skb->len - th->doff*4);
        TCP_SKB_CB(skb)->ack_seq = ntohl(th->ack_seq);
        TCP_SKB_CB(skb)->when = 0;
-       TCP_SKB_CB(skb)->flags = ipv6_get_dsfield(ipv6_hdr(skb));
+       TCP_SKB_CB(skb)->flags = ipv6_get_dsfield(hdr);
        TCP_SKB_CB(skb)->sacked = 0;
 
        sk = __inet6_lookup_skb(&tcp_hashinfo, skb, th->source, th->dest);
@@ -1720,6 +1727,11 @@ process:
        if (sk->sk_state == TCP_TIME_WAIT)
                goto do_time_wait;
 
+       if (hdr->hop_limit < inet6_sk(sk)->min_hopcount) {
+               NET_INC_STATS_BH(net, LINUX_MIB_TCPMINTTLDROP);
+               goto discard_and_relse;
+       }
+
        if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb))
                goto discard_and_relse;