11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * xfrm4_policy.c 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * Changes: 51da177e4SLinus Torvalds * Kazunori MIYAZAWA @USAGI 61da177e4SLinus Torvalds * YOSHIFUJI Hideaki @USAGI 71da177e4SLinus Torvalds * Split up af-specific portion 81da177e4SLinus Torvalds * 91da177e4SLinus Torvalds */ 101da177e4SLinus Torvalds 11aabc9761SHerbert Xu #include <linux/compiler.h> 12aabc9761SHerbert Xu #include <linux/inetdevice.h> 131da177e4SLinus Torvalds #include <net/xfrm.h> 141da177e4SLinus Torvalds #include <net/ip.h> 151da177e4SLinus Torvalds 161da177e4SLinus Torvalds static struct dst_ops xfrm4_dst_ops; 171da177e4SLinus Torvalds static struct xfrm_policy_afinfo xfrm4_policy_afinfo; 181da177e4SLinus Torvalds 191da177e4SLinus Torvalds static int xfrm4_dst_lookup(struct xfrm_dst **dst, struct flowi *fl) 201da177e4SLinus Torvalds { 211da177e4SLinus Torvalds return __ip_route_output_key((struct rtable**)dst, fl); 221da177e4SLinus Torvalds } 231da177e4SLinus Torvalds 24a1e59abfSPatrick McHardy static int xfrm4_get_saddr(xfrm_address_t *saddr, xfrm_address_t *daddr) 25a1e59abfSPatrick McHardy { 26a1e59abfSPatrick McHardy struct rtable *rt; 27a1e59abfSPatrick McHardy struct flowi fl_tunnel = { 28a1e59abfSPatrick McHardy .nl_u = { 29a1e59abfSPatrick McHardy .ip4_u = { 30a1e59abfSPatrick McHardy .daddr = daddr->a4, 31a1e59abfSPatrick McHardy }, 32a1e59abfSPatrick McHardy }, 33a1e59abfSPatrick McHardy }; 34a1e59abfSPatrick McHardy 35a1e59abfSPatrick McHardy if (!xfrm4_dst_lookup((struct xfrm_dst **)&rt, &fl_tunnel)) { 36a1e59abfSPatrick McHardy saddr->a4 = rt->rt_src; 37a1e59abfSPatrick McHardy dst_release(&rt->u.dst); 38a1e59abfSPatrick McHardy return 0; 39a1e59abfSPatrick McHardy } 40a1e59abfSPatrick McHardy return -EHOSTUNREACH; 41a1e59abfSPatrick McHardy } 42a1e59abfSPatrick McHardy 431da177e4SLinus Torvalds static struct dst_entry * 441da177e4SLinus Torvalds __xfrm4_find_bundle(struct flowi *fl, struct xfrm_policy *policy) 451da177e4SLinus Torvalds { 461da177e4SLinus Torvalds struct dst_entry *dst; 471da177e4SLinus Torvalds 481da177e4SLinus Torvalds read_lock_bh(&policy->lock); 491da177e4SLinus Torvalds for (dst = policy->bundles; dst; dst = dst->next) { 501da177e4SLinus Torvalds struct xfrm_dst *xdst = (struct xfrm_dst*)dst; 511da177e4SLinus Torvalds if (xdst->u.rt.fl.oif == fl->oif && /*XXX*/ 521da177e4SLinus Torvalds xdst->u.rt.fl.fl4_dst == fl->fl4_dst && 531da177e4SLinus Torvalds xdst->u.rt.fl.fl4_src == fl->fl4_src && 544da3089fSHerbert Xu xdst->u.rt.fl.fl4_tos == fl->fl4_tos && 555b368e61SVenkat Yekkirala xfrm_bundle_ok(policy, xdst, fl, AF_INET, 0)) { 561da177e4SLinus Torvalds dst_clone(dst); 571da177e4SLinus Torvalds break; 581da177e4SLinus Torvalds } 591da177e4SLinus Torvalds } 601da177e4SLinus Torvalds read_unlock_bh(&policy->lock); 611da177e4SLinus Torvalds return dst; 621da177e4SLinus Torvalds } 631da177e4SLinus Torvalds 641da177e4SLinus Torvalds /* Allocate chain of dst_entry's, attach known xfrm's, calculate 651da177e4SLinus Torvalds * all the metrics... Shortly, bundle a bundle. 661da177e4SLinus Torvalds */ 671da177e4SLinus Torvalds 681da177e4SLinus Torvalds static int 691da177e4SLinus Torvalds __xfrm4_bundle_create(struct xfrm_policy *policy, struct xfrm_state **xfrm, int nx, 701da177e4SLinus Torvalds struct flowi *fl, struct dst_entry **dst_p) 711da177e4SLinus Torvalds { 721da177e4SLinus Torvalds struct dst_entry *dst, *dst_prev; 731da177e4SLinus Torvalds struct rtable *rt0 = (struct rtable*)(*dst_p); 741da177e4SLinus Torvalds struct rtable *rt = rt0; 75*8c689a6eSAl Viro __be32 remote = fl->fl4_dst; 76*8c689a6eSAl Viro __be32 local = fl->fl4_src; 771da177e4SLinus Torvalds struct flowi fl_tunnel = { 781da177e4SLinus Torvalds .nl_u = { 791da177e4SLinus Torvalds .ip4_u = { 801da177e4SLinus Torvalds .saddr = local, 814da3089fSHerbert Xu .daddr = remote, 824da3089fSHerbert Xu .tos = fl->fl4_tos 831da177e4SLinus Torvalds } 841da177e4SLinus Torvalds } 851da177e4SLinus Torvalds }; 861da177e4SLinus Torvalds int i; 871da177e4SLinus Torvalds int err; 881da177e4SLinus Torvalds int header_len = 0; 891da177e4SLinus Torvalds int trailer_len = 0; 901da177e4SLinus Torvalds 911da177e4SLinus Torvalds dst = dst_prev = NULL; 921da177e4SLinus Torvalds dst_hold(&rt->u.dst); 931da177e4SLinus Torvalds 941da177e4SLinus Torvalds for (i = 0; i < nx; i++) { 951da177e4SLinus Torvalds struct dst_entry *dst1 = dst_alloc(&xfrm4_dst_ops); 961da177e4SLinus Torvalds struct xfrm_dst *xdst; 971da177e4SLinus Torvalds int tunnel = 0; 981da177e4SLinus Torvalds 991da177e4SLinus Torvalds if (unlikely(dst1 == NULL)) { 1001da177e4SLinus Torvalds err = -ENOBUFS; 1011da177e4SLinus Torvalds dst_release(&rt->u.dst); 1021da177e4SLinus Torvalds goto error; 1031da177e4SLinus Torvalds } 1041da177e4SLinus Torvalds 1051da177e4SLinus Torvalds if (!dst) 1061da177e4SLinus Torvalds dst = dst1; 1071da177e4SLinus Torvalds else { 1081da177e4SLinus Torvalds dst_prev->child = dst1; 1091da177e4SLinus Torvalds dst1->flags |= DST_NOHASH; 1101da177e4SLinus Torvalds dst_clone(dst1); 1111da177e4SLinus Torvalds } 1121da177e4SLinus Torvalds 1131da177e4SLinus Torvalds xdst = (struct xfrm_dst *)dst1; 1141da177e4SLinus Torvalds xdst->route = &rt->u.dst; 1159d4a706dSDavid S. Miller xdst->genid = xfrm[i]->genid; 1161da177e4SLinus Torvalds 1171da177e4SLinus Torvalds dst1->next = dst_prev; 1181da177e4SLinus Torvalds dst_prev = dst1; 1197e49e6deSMasahide NAKAMURA if (xfrm[i]->props.mode != XFRM_MODE_TRANSPORT) { 1201da177e4SLinus Torvalds remote = xfrm[i]->id.daddr.a4; 1211da177e4SLinus Torvalds local = xfrm[i]->props.saddr.a4; 1221da177e4SLinus Torvalds tunnel = 1; 1231da177e4SLinus Torvalds } 1241da177e4SLinus Torvalds header_len += xfrm[i]->props.header_len; 1251da177e4SLinus Torvalds trailer_len += xfrm[i]->props.trailer_len; 1261da177e4SLinus Torvalds 1271da177e4SLinus Torvalds if (tunnel) { 1281da177e4SLinus Torvalds fl_tunnel.fl4_src = local; 1291da177e4SLinus Torvalds fl_tunnel.fl4_dst = remote; 1301da177e4SLinus Torvalds err = xfrm_dst_lookup((struct xfrm_dst **)&rt, 1311da177e4SLinus Torvalds &fl_tunnel, AF_INET); 1321da177e4SLinus Torvalds if (err) 1331da177e4SLinus Torvalds goto error; 1341da177e4SLinus Torvalds } else 1351da177e4SLinus Torvalds dst_hold(&rt->u.dst); 1361da177e4SLinus Torvalds } 1371da177e4SLinus Torvalds 1381da177e4SLinus Torvalds dst_prev->child = &rt->u.dst; 1391da177e4SLinus Torvalds dst->path = &rt->u.dst; 1401da177e4SLinus Torvalds 1411da177e4SLinus Torvalds *dst_p = dst; 1421da177e4SLinus Torvalds dst = dst_prev; 1431da177e4SLinus Torvalds 1441da177e4SLinus Torvalds dst_prev = *dst_p; 1451da177e4SLinus Torvalds i = 0; 1461da177e4SLinus Torvalds for (; dst_prev != &rt->u.dst; dst_prev = dst_prev->child) { 1471da177e4SLinus Torvalds struct xfrm_dst *x = (struct xfrm_dst*)dst_prev; 1481da177e4SLinus Torvalds x->u.rt.fl = *fl; 1491da177e4SLinus Torvalds 1501da177e4SLinus Torvalds dst_prev->xfrm = xfrm[i++]; 1511da177e4SLinus Torvalds dst_prev->dev = rt->u.dst.dev; 1521da177e4SLinus Torvalds if (rt->u.dst.dev) 1531da177e4SLinus Torvalds dev_hold(rt->u.dst.dev); 1541da177e4SLinus Torvalds dst_prev->obsolete = -1; 1551da177e4SLinus Torvalds dst_prev->flags |= DST_HOST; 1561da177e4SLinus Torvalds dst_prev->lastuse = jiffies; 1571da177e4SLinus Torvalds dst_prev->header_len = header_len; 1581b5c2299SMasahide NAKAMURA dst_prev->nfheader_len = 0; 1591da177e4SLinus Torvalds dst_prev->trailer_len = trailer_len; 1601da177e4SLinus Torvalds memcpy(&dst_prev->metrics, &x->route->metrics, sizeof(dst_prev->metrics)); 1611da177e4SLinus Torvalds 1621da177e4SLinus Torvalds /* Copy neighbout for reachability confirmation */ 1631da177e4SLinus Torvalds dst_prev->neighbour = neigh_clone(rt->u.dst.neighbour); 1641da177e4SLinus Torvalds dst_prev->input = rt->u.dst.input; 1651da177e4SLinus Torvalds dst_prev->output = xfrm4_output; 1661da177e4SLinus Torvalds if (rt->peer) 1671da177e4SLinus Torvalds atomic_inc(&rt->peer->refcnt); 1681da177e4SLinus Torvalds x->u.rt.peer = rt->peer; 1691da177e4SLinus Torvalds /* Sheit... I remember I did this right. Apparently, 1701da177e4SLinus Torvalds * it was magically lost, so this code needs audit */ 1711da177e4SLinus Torvalds x->u.rt.rt_flags = rt0->rt_flags&(RTCF_BROADCAST|RTCF_MULTICAST|RTCF_LOCAL); 1721da177e4SLinus Torvalds x->u.rt.rt_type = rt->rt_type; 1731da177e4SLinus Torvalds x->u.rt.rt_src = rt0->rt_src; 1741da177e4SLinus Torvalds x->u.rt.rt_dst = rt0->rt_dst; 1751da177e4SLinus Torvalds x->u.rt.rt_gateway = rt->rt_gateway; 1761da177e4SLinus Torvalds x->u.rt.rt_spec_dst = rt0->rt_spec_dst; 177aabc9761SHerbert Xu x->u.rt.idev = rt0->idev; 178aabc9761SHerbert Xu in_dev_hold(rt0->idev); 1791da177e4SLinus Torvalds header_len -= x->u.dst.xfrm->props.header_len; 1801da177e4SLinus Torvalds trailer_len -= x->u.dst.xfrm->props.trailer_len; 1811da177e4SLinus Torvalds } 1821da177e4SLinus Torvalds 1831da177e4SLinus Torvalds xfrm_init_pmtu(dst); 1841da177e4SLinus Torvalds return 0; 1851da177e4SLinus Torvalds 1861da177e4SLinus Torvalds error: 1871da177e4SLinus Torvalds if (dst) 1881da177e4SLinus Torvalds dst_free(dst); 1891da177e4SLinus Torvalds return err; 1901da177e4SLinus Torvalds } 1911da177e4SLinus Torvalds 1921da177e4SLinus Torvalds static void 1931da177e4SLinus Torvalds _decode_session4(struct sk_buff *skb, struct flowi *fl) 1941da177e4SLinus Torvalds { 1951da177e4SLinus Torvalds struct iphdr *iph = skb->nh.iph; 1961da177e4SLinus Torvalds u8 *xprth = skb->nh.raw + iph->ihl*4; 1971da177e4SLinus Torvalds 1981da177e4SLinus Torvalds memset(fl, 0, sizeof(struct flowi)); 1991da177e4SLinus Torvalds if (!(iph->frag_off & htons(IP_MF | IP_OFFSET))) { 2001da177e4SLinus Torvalds switch (iph->protocol) { 2011da177e4SLinus Torvalds case IPPROTO_UDP: 2021da177e4SLinus Torvalds case IPPROTO_TCP: 2031da177e4SLinus Torvalds case IPPROTO_SCTP: 2049e999993SPatrick McHardy case IPPROTO_DCCP: 2051da177e4SLinus Torvalds if (pskb_may_pull(skb, xprth + 4 - skb->data)) { 206*8c689a6eSAl Viro __be16 *ports = (__be16 *)xprth; 2071da177e4SLinus Torvalds 2081da177e4SLinus Torvalds fl->fl_ip_sport = ports[0]; 2091da177e4SLinus Torvalds fl->fl_ip_dport = ports[1]; 2101da177e4SLinus Torvalds } 2111da177e4SLinus Torvalds break; 2121da177e4SLinus Torvalds 2131da177e4SLinus Torvalds case IPPROTO_ICMP: 2141da177e4SLinus Torvalds if (pskb_may_pull(skb, xprth + 2 - skb->data)) { 2151da177e4SLinus Torvalds u8 *icmp = xprth; 2161da177e4SLinus Torvalds 2171da177e4SLinus Torvalds fl->fl_icmp_type = icmp[0]; 2181da177e4SLinus Torvalds fl->fl_icmp_code = icmp[1]; 2191da177e4SLinus Torvalds } 2201da177e4SLinus Torvalds break; 2211da177e4SLinus Torvalds 2221da177e4SLinus Torvalds case IPPROTO_ESP: 2231da177e4SLinus Torvalds if (pskb_may_pull(skb, xprth + 4 - skb->data)) { 2244324a174SAl Viro __be32 *ehdr = (__be32 *)xprth; 2251da177e4SLinus Torvalds 2261da177e4SLinus Torvalds fl->fl_ipsec_spi = ehdr[0]; 2271da177e4SLinus Torvalds } 2281da177e4SLinus Torvalds break; 2291da177e4SLinus Torvalds 2301da177e4SLinus Torvalds case IPPROTO_AH: 2311da177e4SLinus Torvalds if (pskb_may_pull(skb, xprth + 8 - skb->data)) { 2324324a174SAl Viro __be32 *ah_hdr = (__be32*)xprth; 2331da177e4SLinus Torvalds 2341da177e4SLinus Torvalds fl->fl_ipsec_spi = ah_hdr[1]; 2351da177e4SLinus Torvalds } 2361da177e4SLinus Torvalds break; 2371da177e4SLinus Torvalds 2381da177e4SLinus Torvalds case IPPROTO_COMP: 2391da177e4SLinus Torvalds if (pskb_may_pull(skb, xprth + 4 - skb->data)) { 2404324a174SAl Viro __be16 *ipcomp_hdr = (__be16 *)xprth; 2411da177e4SLinus Torvalds 2424195f814SAlexey Dobriyan fl->fl_ipsec_spi = htonl(ntohs(ipcomp_hdr[1])); 2431da177e4SLinus Torvalds } 2441da177e4SLinus Torvalds break; 2451da177e4SLinus Torvalds default: 2461da177e4SLinus Torvalds fl->fl_ipsec_spi = 0; 2471da177e4SLinus Torvalds break; 2481da177e4SLinus Torvalds }; 2491da177e4SLinus Torvalds } 2501da177e4SLinus Torvalds fl->proto = iph->protocol; 2511da177e4SLinus Torvalds fl->fl4_dst = iph->daddr; 2521da177e4SLinus Torvalds fl->fl4_src = iph->saddr; 2534da3089fSHerbert Xu fl->fl4_tos = iph->tos; 2541da177e4SLinus Torvalds } 2551da177e4SLinus Torvalds 2561da177e4SLinus Torvalds static inline int xfrm4_garbage_collect(void) 2571da177e4SLinus Torvalds { 2581da177e4SLinus Torvalds xfrm4_policy_afinfo.garbage_collect(); 2591da177e4SLinus Torvalds return (atomic_read(&xfrm4_dst_ops.entries) > xfrm4_dst_ops.gc_thresh*2); 2601da177e4SLinus Torvalds } 2611da177e4SLinus Torvalds 2621da177e4SLinus Torvalds static void xfrm4_update_pmtu(struct dst_entry *dst, u32 mtu) 2631da177e4SLinus Torvalds { 2641da177e4SLinus Torvalds struct xfrm_dst *xdst = (struct xfrm_dst *)dst; 2651da177e4SLinus Torvalds struct dst_entry *path = xdst->route; 2661da177e4SLinus Torvalds 2671da177e4SLinus Torvalds path->ops->update_pmtu(path, mtu); 2681da177e4SLinus Torvalds } 2691da177e4SLinus Torvalds 270aabc9761SHerbert Xu static void xfrm4_dst_destroy(struct dst_entry *dst) 271aabc9761SHerbert Xu { 272aabc9761SHerbert Xu struct xfrm_dst *xdst = (struct xfrm_dst *)dst; 273aabc9761SHerbert Xu 274aabc9761SHerbert Xu if (likely(xdst->u.rt.idev)) 275aabc9761SHerbert Xu in_dev_put(xdst->u.rt.idev); 276aabc9761SHerbert Xu xfrm_dst_destroy(xdst); 277aabc9761SHerbert Xu } 278aabc9761SHerbert Xu 279aabc9761SHerbert Xu static void xfrm4_dst_ifdown(struct dst_entry *dst, struct net_device *dev, 280aabc9761SHerbert Xu int unregister) 281aabc9761SHerbert Xu { 282aabc9761SHerbert Xu struct xfrm_dst *xdst; 283aabc9761SHerbert Xu 284aabc9761SHerbert Xu if (!unregister) 285aabc9761SHerbert Xu return; 286aabc9761SHerbert Xu 287aabc9761SHerbert Xu xdst = (struct xfrm_dst *)dst; 288aabc9761SHerbert Xu if (xdst->u.rt.idev->dev == dev) { 289aabc9761SHerbert Xu struct in_device *loopback_idev = in_dev_get(&loopback_dev); 290aabc9761SHerbert Xu BUG_ON(!loopback_idev); 291aabc9761SHerbert Xu 292aabc9761SHerbert Xu do { 293aabc9761SHerbert Xu in_dev_put(xdst->u.rt.idev); 294aabc9761SHerbert Xu xdst->u.rt.idev = loopback_idev; 295aabc9761SHerbert Xu in_dev_hold(loopback_idev); 296aabc9761SHerbert Xu xdst = (struct xfrm_dst *)xdst->u.dst.child; 297aabc9761SHerbert Xu } while (xdst->u.dst.xfrm); 298aabc9761SHerbert Xu 299aabc9761SHerbert Xu __in_dev_put(loopback_idev); 300aabc9761SHerbert Xu } 301aabc9761SHerbert Xu 302aabc9761SHerbert Xu xfrm_dst_ifdown(dst, dev); 303aabc9761SHerbert Xu } 304aabc9761SHerbert Xu 3051da177e4SLinus Torvalds static struct dst_ops xfrm4_dst_ops = { 3061da177e4SLinus Torvalds .family = AF_INET, 3071da177e4SLinus Torvalds .protocol = __constant_htons(ETH_P_IP), 3081da177e4SLinus Torvalds .gc = xfrm4_garbage_collect, 3091da177e4SLinus Torvalds .update_pmtu = xfrm4_update_pmtu, 310aabc9761SHerbert Xu .destroy = xfrm4_dst_destroy, 311aabc9761SHerbert Xu .ifdown = xfrm4_dst_ifdown, 3121da177e4SLinus Torvalds .gc_thresh = 1024, 3131da177e4SLinus Torvalds .entry_size = sizeof(struct xfrm_dst), 3141da177e4SLinus Torvalds }; 3151da177e4SLinus Torvalds 3161da177e4SLinus Torvalds static struct xfrm_policy_afinfo xfrm4_policy_afinfo = { 3171da177e4SLinus Torvalds .family = AF_INET, 3181da177e4SLinus Torvalds .dst_ops = &xfrm4_dst_ops, 3191da177e4SLinus Torvalds .dst_lookup = xfrm4_dst_lookup, 320a1e59abfSPatrick McHardy .get_saddr = xfrm4_get_saddr, 3211da177e4SLinus Torvalds .find_bundle = __xfrm4_find_bundle, 3221da177e4SLinus Torvalds .bundle_create = __xfrm4_bundle_create, 3231da177e4SLinus Torvalds .decode_session = _decode_session4, 3241da177e4SLinus Torvalds }; 3251da177e4SLinus Torvalds 3261da177e4SLinus Torvalds static void __init xfrm4_policy_init(void) 3271da177e4SLinus Torvalds { 3281da177e4SLinus Torvalds xfrm_policy_register_afinfo(&xfrm4_policy_afinfo); 3291da177e4SLinus Torvalds } 3301da177e4SLinus Torvalds 3311da177e4SLinus Torvalds static void __exit xfrm4_policy_fini(void) 3321da177e4SLinus Torvalds { 3331da177e4SLinus Torvalds xfrm_policy_unregister_afinfo(&xfrm4_policy_afinfo); 3341da177e4SLinus Torvalds } 3351da177e4SLinus Torvalds 3361da177e4SLinus Torvalds void __init xfrm4_init(void) 3371da177e4SLinus Torvalds { 3381da177e4SLinus Torvalds xfrm4_state_init(); 3391da177e4SLinus Torvalds xfrm4_policy_init(); 3401da177e4SLinus Torvalds } 3411da177e4SLinus Torvalds 342