11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * xfrm4_input.c 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * Changes: 51da177e4SLinus Torvalds * YOSHIFUJI Hideaki @USAGI 61da177e4SLinus Torvalds * Split up af-specific portion 71da177e4SLinus Torvalds * Derek Atkins <derek@ihtfp.com> 81da177e4SLinus Torvalds * Add Encapsulation support 91da177e4SLinus Torvalds * 101da177e4SLinus Torvalds */ 111da177e4SLinus Torvalds 121da177e4SLinus Torvalds #include <linux/module.h> 131da177e4SLinus Torvalds #include <linux/string.h> 14*b05e1066SPatrick McHardy #include <linux/netfilter.h> 15*b05e1066SPatrick McHardy #include <linux/netfilter_ipv4.h> 161da177e4SLinus Torvalds #include <net/inet_ecn.h> 171da177e4SLinus Torvalds #include <net/ip.h> 181da177e4SLinus Torvalds #include <net/xfrm.h> 191da177e4SLinus Torvalds 201da177e4SLinus Torvalds int xfrm4_rcv(struct sk_buff *skb) 211da177e4SLinus Torvalds { 221da177e4SLinus Torvalds return xfrm4_rcv_encap(skb, 0); 231da177e4SLinus Torvalds } 241da177e4SLinus Torvalds 251da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm4_rcv); 261da177e4SLinus Torvalds 271da177e4SLinus Torvalds static inline void ipip_ecn_decapsulate(struct sk_buff *skb) 281da177e4SLinus Torvalds { 291da177e4SLinus Torvalds struct iphdr *outer_iph = skb->nh.iph; 301da177e4SLinus Torvalds struct iphdr *inner_iph = skb->h.ipiph; 311da177e4SLinus Torvalds 321da177e4SLinus Torvalds if (INET_ECN_is_ce(outer_iph->tos)) 331da177e4SLinus Torvalds IP_ECN_set_ce(inner_iph); 341da177e4SLinus Torvalds } 351da177e4SLinus Torvalds 361da177e4SLinus Torvalds static int xfrm4_parse_spi(struct sk_buff *skb, u8 nexthdr, u32 *spi, u32 *seq) 371da177e4SLinus Torvalds { 381da177e4SLinus Torvalds switch (nexthdr) { 391da177e4SLinus Torvalds case IPPROTO_IPIP: 401da177e4SLinus Torvalds if (!pskb_may_pull(skb, sizeof(struct iphdr))) 411da177e4SLinus Torvalds return -EINVAL; 421da177e4SLinus Torvalds *spi = skb->nh.iph->saddr; 431da177e4SLinus Torvalds *seq = 0; 441da177e4SLinus Torvalds return 0; 451da177e4SLinus Torvalds } 461da177e4SLinus Torvalds 471da177e4SLinus Torvalds return xfrm_parse_spi(skb, nexthdr, spi, seq); 481da177e4SLinus Torvalds } 491da177e4SLinus Torvalds 50*b05e1066SPatrick McHardy #ifdef CONFIG_NETFILTER 51*b05e1066SPatrick McHardy static inline int xfrm4_rcv_encap_finish(struct sk_buff *skb) 52*b05e1066SPatrick McHardy { 53*b05e1066SPatrick McHardy struct iphdr *iph = skb->nh.iph; 54*b05e1066SPatrick McHardy 55*b05e1066SPatrick McHardy if (skb->dst == NULL) { 56*b05e1066SPatrick McHardy if (ip_route_input(skb, iph->daddr, iph->saddr, iph->tos, 57*b05e1066SPatrick McHardy skb->dev)) 58*b05e1066SPatrick McHardy goto drop; 59*b05e1066SPatrick McHardy } 60*b05e1066SPatrick McHardy return dst_input(skb); 61*b05e1066SPatrick McHardy drop: 62*b05e1066SPatrick McHardy kfree_skb(skb); 63*b05e1066SPatrick McHardy return NET_RX_DROP; 64*b05e1066SPatrick McHardy } 65*b05e1066SPatrick McHardy #endif 66*b05e1066SPatrick McHardy 671da177e4SLinus Torvalds int xfrm4_rcv_encap(struct sk_buff *skb, __u16 encap_type) 681da177e4SLinus Torvalds { 691da177e4SLinus Torvalds int err; 701da177e4SLinus Torvalds u32 spi, seq; 711da177e4SLinus Torvalds struct sec_decap_state xfrm_vec[XFRM_MAX_DEPTH]; 721da177e4SLinus Torvalds struct xfrm_state *x; 731da177e4SLinus Torvalds int xfrm_nr = 0; 741da177e4SLinus Torvalds int decaps = 0; 751da177e4SLinus Torvalds 761da177e4SLinus Torvalds if ((err = xfrm4_parse_spi(skb, skb->nh.iph->protocol, &spi, &seq)) != 0) 771da177e4SLinus Torvalds goto drop; 781da177e4SLinus Torvalds 791da177e4SLinus Torvalds do { 801da177e4SLinus Torvalds struct iphdr *iph = skb->nh.iph; 811da177e4SLinus Torvalds 821da177e4SLinus Torvalds if (xfrm_nr == XFRM_MAX_DEPTH) 831da177e4SLinus Torvalds goto drop; 841da177e4SLinus Torvalds 851da177e4SLinus Torvalds x = xfrm_state_lookup((xfrm_address_t *)&iph->daddr, spi, iph->protocol, AF_INET); 861da177e4SLinus Torvalds if (x == NULL) 871da177e4SLinus Torvalds goto drop; 881da177e4SLinus Torvalds 891da177e4SLinus Torvalds spin_lock(&x->lock); 901da177e4SLinus Torvalds if (unlikely(x->km.state != XFRM_STATE_VALID)) 911da177e4SLinus Torvalds goto drop_unlock; 921da177e4SLinus Torvalds 931da177e4SLinus Torvalds if (x->props.replay_window && xfrm_replay_check(x, seq)) 941da177e4SLinus Torvalds goto drop_unlock; 951da177e4SLinus Torvalds 961da177e4SLinus Torvalds if (xfrm_state_check_expire(x)) 971da177e4SLinus Torvalds goto drop_unlock; 981da177e4SLinus Torvalds 991da177e4SLinus Torvalds xfrm_vec[xfrm_nr].decap.decap_type = encap_type; 1001da177e4SLinus Torvalds if (x->type->input(x, &(xfrm_vec[xfrm_nr].decap), skb)) 1011da177e4SLinus Torvalds goto drop_unlock; 1021da177e4SLinus Torvalds 1031da177e4SLinus Torvalds /* only the first xfrm gets the encap type */ 1041da177e4SLinus Torvalds encap_type = 0; 1051da177e4SLinus Torvalds 1061da177e4SLinus Torvalds if (x->props.replay_window) 1071da177e4SLinus Torvalds xfrm_replay_advance(x, seq); 1081da177e4SLinus Torvalds 1091da177e4SLinus Torvalds x->curlft.bytes += skb->len; 1101da177e4SLinus Torvalds x->curlft.packets++; 1111da177e4SLinus Torvalds 1121da177e4SLinus Torvalds spin_unlock(&x->lock); 1131da177e4SLinus Torvalds 1141da177e4SLinus Torvalds xfrm_vec[xfrm_nr++].xvec = x; 1151da177e4SLinus Torvalds 1161da177e4SLinus Torvalds iph = skb->nh.iph; 1171da177e4SLinus Torvalds 1181da177e4SLinus Torvalds if (x->props.mode) { 1191da177e4SLinus Torvalds if (iph->protocol != IPPROTO_IPIP) 1201da177e4SLinus Torvalds goto drop; 1211da177e4SLinus Torvalds if (!pskb_may_pull(skb, sizeof(struct iphdr))) 1221da177e4SLinus Torvalds goto drop; 1231da177e4SLinus Torvalds if (skb_cloned(skb) && 1241da177e4SLinus Torvalds pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) 1251da177e4SLinus Torvalds goto drop; 1261da177e4SLinus Torvalds if (x->props.flags & XFRM_STATE_DECAP_DSCP) 1271da177e4SLinus Torvalds ipv4_copy_dscp(iph, skb->h.ipiph); 1281da177e4SLinus Torvalds if (!(x->props.flags & XFRM_STATE_NOECN)) 1291da177e4SLinus Torvalds ipip_ecn_decapsulate(skb); 1301da177e4SLinus Torvalds skb->mac.raw = memmove(skb->data - skb->mac_len, 1311da177e4SLinus Torvalds skb->mac.raw, skb->mac_len); 1321da177e4SLinus Torvalds skb->nh.raw = skb->data; 1331da177e4SLinus Torvalds memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options)); 1341da177e4SLinus Torvalds decaps = 1; 1351da177e4SLinus Torvalds break; 1361da177e4SLinus Torvalds } 1371da177e4SLinus Torvalds 1381da177e4SLinus Torvalds if ((err = xfrm_parse_spi(skb, skb->nh.iph->protocol, &spi, &seq)) < 0) 1391da177e4SLinus Torvalds goto drop; 1401da177e4SLinus Torvalds } while (!err); 1411da177e4SLinus Torvalds 1421da177e4SLinus Torvalds /* Allocate new secpath or COW existing one. */ 1431da177e4SLinus Torvalds 1441da177e4SLinus Torvalds if (!skb->sp || atomic_read(&skb->sp->refcnt) != 1) { 1451da177e4SLinus Torvalds struct sec_path *sp; 1461da177e4SLinus Torvalds sp = secpath_dup(skb->sp); 1471da177e4SLinus Torvalds if (!sp) 1481da177e4SLinus Torvalds goto drop; 1491da177e4SLinus Torvalds if (skb->sp) 1501da177e4SLinus Torvalds secpath_put(skb->sp); 1511da177e4SLinus Torvalds skb->sp = sp; 1521da177e4SLinus Torvalds } 1531da177e4SLinus Torvalds if (xfrm_nr + skb->sp->len > XFRM_MAX_DEPTH) 1541da177e4SLinus Torvalds goto drop; 1551da177e4SLinus Torvalds 1561da177e4SLinus Torvalds memcpy(skb->sp->x+skb->sp->len, xfrm_vec, xfrm_nr*sizeof(struct sec_decap_state)); 1571da177e4SLinus Torvalds skb->sp->len += xfrm_nr; 1581da177e4SLinus Torvalds 159*b05e1066SPatrick McHardy nf_reset(skb); 160*b05e1066SPatrick McHardy 1611da177e4SLinus Torvalds if (decaps) { 1621da177e4SLinus Torvalds if (!(skb->dev->flags&IFF_LOOPBACK)) { 1631da177e4SLinus Torvalds dst_release(skb->dst); 1641da177e4SLinus Torvalds skb->dst = NULL; 1651da177e4SLinus Torvalds } 1661da177e4SLinus Torvalds netif_rx(skb); 1671da177e4SLinus Torvalds return 0; 1681da177e4SLinus Torvalds } else { 169*b05e1066SPatrick McHardy #ifdef CONFIG_NETFILTER 170*b05e1066SPatrick McHardy __skb_push(skb, skb->data - skb->nh.raw); 171*b05e1066SPatrick McHardy skb->nh.iph->tot_len = htons(skb->len); 172*b05e1066SPatrick McHardy ip_send_check(skb->nh.iph); 173*b05e1066SPatrick McHardy 174*b05e1066SPatrick McHardy NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, skb->dev, NULL, 175*b05e1066SPatrick McHardy xfrm4_rcv_encap_finish); 176*b05e1066SPatrick McHardy return 0; 177*b05e1066SPatrick McHardy #else 1781da177e4SLinus Torvalds return -skb->nh.iph->protocol; 179*b05e1066SPatrick McHardy #endif 1801da177e4SLinus Torvalds } 1811da177e4SLinus Torvalds 1821da177e4SLinus Torvalds drop_unlock: 1831da177e4SLinus Torvalds spin_unlock(&x->lock); 1841da177e4SLinus Torvalds xfrm_state_put(x); 1851da177e4SLinus Torvalds drop: 1861da177e4SLinus Torvalds while (--xfrm_nr >= 0) 1871da177e4SLinus Torvalds xfrm_state_put(xfrm_vec[xfrm_nr].xvec); 1881da177e4SLinus Torvalds 1891da177e4SLinus Torvalds kfree_skb(skb); 1901da177e4SLinus Torvalds return 0; 1911da177e4SLinus Torvalds } 192