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 11*66cdb3caSHerbert Xu #include <linux/err.h> 12*66cdb3caSHerbert Xu #include <linux/kernel.h> 13aabc9761SHerbert Xu #include <linux/inetdevice.h> 1445ff5a3fSHerbert Xu #include <net/dst.h> 151da177e4SLinus Torvalds #include <net/xfrm.h> 161da177e4SLinus Torvalds #include <net/ip.h> 171da177e4SLinus Torvalds 181da177e4SLinus Torvalds static struct dst_ops xfrm4_dst_ops; 191da177e4SLinus Torvalds static struct xfrm_policy_afinfo xfrm4_policy_afinfo; 201da177e4SLinus Torvalds 21*66cdb3caSHerbert Xu static struct dst_entry *xfrm4_dst_lookup(int tos, xfrm_address_t *saddr, 22*66cdb3caSHerbert Xu xfrm_address_t *daddr) 231da177e4SLinus Torvalds { 24*66cdb3caSHerbert Xu struct flowi fl = { 25a1e59abfSPatrick McHardy .nl_u = { 26a1e59abfSPatrick McHardy .ip4_u = { 27*66cdb3caSHerbert Xu .tos = tos, 28a1e59abfSPatrick McHardy .daddr = daddr->a4, 29a1e59abfSPatrick McHardy }, 30a1e59abfSPatrick McHardy }, 31a1e59abfSPatrick McHardy }; 32*66cdb3caSHerbert Xu struct dst_entry *dst; 33*66cdb3caSHerbert Xu struct rtable *rt; 34*66cdb3caSHerbert Xu int err; 35a1e59abfSPatrick McHardy 36*66cdb3caSHerbert Xu if (saddr) 37*66cdb3caSHerbert Xu fl.fl4_src = saddr->a4; 38*66cdb3caSHerbert Xu 39*66cdb3caSHerbert Xu err = __ip_route_output_key(&rt, &fl); 40*66cdb3caSHerbert Xu dst = &rt->u.dst; 41*66cdb3caSHerbert Xu if (err) 42*66cdb3caSHerbert Xu dst = ERR_PTR(err); 43*66cdb3caSHerbert Xu return dst; 44a1e59abfSPatrick McHardy } 45*66cdb3caSHerbert Xu 46*66cdb3caSHerbert Xu static int xfrm4_get_saddr(xfrm_address_t *saddr, xfrm_address_t *daddr) 47*66cdb3caSHerbert Xu { 48*66cdb3caSHerbert Xu struct dst_entry *dst; 49*66cdb3caSHerbert Xu struct rtable *rt; 50*66cdb3caSHerbert Xu 51*66cdb3caSHerbert Xu dst = xfrm4_dst_lookup(0, NULL, daddr); 52*66cdb3caSHerbert Xu if (IS_ERR(dst)) 53a1e59abfSPatrick McHardy return -EHOSTUNREACH; 54*66cdb3caSHerbert Xu 55*66cdb3caSHerbert Xu rt = (struct rtable *)dst; 56*66cdb3caSHerbert Xu saddr->a4 = rt->rt_src; 57*66cdb3caSHerbert Xu dst_release(dst); 58*66cdb3caSHerbert Xu return 0; 59a1e59abfSPatrick McHardy } 60a1e59abfSPatrick McHardy 611da177e4SLinus Torvalds static struct dst_entry * 621da177e4SLinus Torvalds __xfrm4_find_bundle(struct flowi *fl, struct xfrm_policy *policy) 631da177e4SLinus Torvalds { 641da177e4SLinus Torvalds struct dst_entry *dst; 651da177e4SLinus Torvalds 661da177e4SLinus Torvalds read_lock_bh(&policy->lock); 671da177e4SLinus Torvalds for (dst = policy->bundles; dst; dst = dst->next) { 681da177e4SLinus Torvalds struct xfrm_dst *xdst = (struct xfrm_dst*)dst; 691da177e4SLinus Torvalds if (xdst->u.rt.fl.oif == fl->oif && /*XXX*/ 701da177e4SLinus Torvalds xdst->u.rt.fl.fl4_dst == fl->fl4_dst && 711da177e4SLinus Torvalds xdst->u.rt.fl.fl4_src == fl->fl4_src && 724da3089fSHerbert Xu xdst->u.rt.fl.fl4_tos == fl->fl4_tos && 735b368e61SVenkat Yekkirala xfrm_bundle_ok(policy, xdst, fl, AF_INET, 0)) { 741da177e4SLinus Torvalds dst_clone(dst); 751da177e4SLinus Torvalds break; 761da177e4SLinus Torvalds } 771da177e4SLinus Torvalds } 781da177e4SLinus Torvalds read_unlock_bh(&policy->lock); 791da177e4SLinus Torvalds return dst; 801da177e4SLinus Torvalds } 811da177e4SLinus Torvalds 821da177e4SLinus Torvalds /* Allocate chain of dst_entry's, attach known xfrm's, calculate 831da177e4SLinus Torvalds * all the metrics... Shortly, bundle a bundle. 841da177e4SLinus Torvalds */ 851da177e4SLinus Torvalds 861da177e4SLinus Torvalds static int 871da177e4SLinus Torvalds __xfrm4_bundle_create(struct xfrm_policy *policy, struct xfrm_state **xfrm, int nx, 881da177e4SLinus Torvalds struct flowi *fl, struct dst_entry **dst_p) 891da177e4SLinus Torvalds { 901da177e4SLinus Torvalds struct dst_entry *dst, *dst_prev; 911da177e4SLinus Torvalds struct rtable *rt0 = (struct rtable*)(*dst_p); 921da177e4SLinus Torvalds struct rtable *rt = rt0; 93*66cdb3caSHerbert Xu int tos = fl->fl4_tos; 941da177e4SLinus Torvalds int i; 951da177e4SLinus Torvalds int err; 961da177e4SLinus Torvalds int header_len = 0; 971da177e4SLinus Torvalds int trailer_len = 0; 981da177e4SLinus Torvalds 991da177e4SLinus Torvalds dst = dst_prev = NULL; 1001da177e4SLinus Torvalds dst_hold(&rt->u.dst); 1011da177e4SLinus Torvalds 1021da177e4SLinus Torvalds for (i = 0; i < nx; i++) { 1031da177e4SLinus Torvalds struct dst_entry *dst1 = dst_alloc(&xfrm4_dst_ops); 1041da177e4SLinus Torvalds struct xfrm_dst *xdst; 1051da177e4SLinus Torvalds 1061da177e4SLinus Torvalds if (unlikely(dst1 == NULL)) { 1071da177e4SLinus Torvalds err = -ENOBUFS; 1081da177e4SLinus Torvalds dst_release(&rt->u.dst); 1091da177e4SLinus Torvalds goto error; 1101da177e4SLinus Torvalds } 1111da177e4SLinus Torvalds 1121da177e4SLinus Torvalds if (!dst) 1131da177e4SLinus Torvalds dst = dst1; 1141da177e4SLinus Torvalds else { 1151da177e4SLinus Torvalds dst_prev->child = dst1; 1161da177e4SLinus Torvalds dst1->flags |= DST_NOHASH; 1171da177e4SLinus Torvalds dst_clone(dst1); 1181da177e4SLinus Torvalds } 1191da177e4SLinus Torvalds 1201da177e4SLinus Torvalds xdst = (struct xfrm_dst *)dst1; 1211da177e4SLinus Torvalds xdst->route = &rt->u.dst; 1229d4a706dSDavid S. Miller xdst->genid = xfrm[i]->genid; 1231da177e4SLinus Torvalds 1241da177e4SLinus Torvalds dst1->next = dst_prev; 1251da177e4SLinus Torvalds dst_prev = dst1; 12643372262SMiika Komu 1271da177e4SLinus Torvalds header_len += xfrm[i]->props.header_len; 1281da177e4SLinus Torvalds trailer_len += xfrm[i]->props.trailer_len; 1291da177e4SLinus Torvalds 1301bfcb10fSHerbert Xu if (xfrm[i]->props.mode != XFRM_MODE_TRANSPORT) { 131*66cdb3caSHerbert Xu dst1 = xfrm_dst_lookup(xfrm[i], tos); 132*66cdb3caSHerbert Xu err = PTR_ERR(dst1); 133*66cdb3caSHerbert Xu if (IS_ERR(dst1)) 1341da177e4SLinus Torvalds goto error; 135*66cdb3caSHerbert Xu 136*66cdb3caSHerbert Xu rt = (struct rtable *)dst1; 1371da177e4SLinus Torvalds } else 1381da177e4SLinus Torvalds dst_hold(&rt->u.dst); 1391da177e4SLinus Torvalds } 1401da177e4SLinus Torvalds 1411da177e4SLinus Torvalds dst_prev->child = &rt->u.dst; 1421da177e4SLinus Torvalds dst->path = &rt->u.dst; 1431da177e4SLinus Torvalds 1448ce68cebSHerbert Xu /* Copy neighbout for reachability confirmation */ 1458ce68cebSHerbert Xu dst->neighbour = neigh_clone(rt->u.dst.neighbour); 1468ce68cebSHerbert Xu 1471da177e4SLinus Torvalds *dst_p = dst; 1481da177e4SLinus Torvalds dst = dst_prev; 1491da177e4SLinus Torvalds 1501da177e4SLinus Torvalds dst_prev = *dst_p; 1511da177e4SLinus Torvalds i = 0; 152fff69388SHerbert Xu err = -ENODEV; 1531da177e4SLinus Torvalds for (; dst_prev != &rt->u.dst; dst_prev = dst_prev->child) { 1541da177e4SLinus Torvalds struct xfrm_dst *x = (struct xfrm_dst*)dst_prev; 1551da177e4SLinus Torvalds x->u.rt.fl = *fl; 1561da177e4SLinus Torvalds 1571da177e4SLinus Torvalds dst_prev->xfrm = xfrm[i++]; 1581da177e4SLinus Torvalds dst_prev->dev = rt->u.dst.dev; 159fff69388SHerbert Xu if (!rt->u.dst.dev) 160fff69388SHerbert Xu goto error; 1611da177e4SLinus Torvalds dev_hold(rt->u.dst.dev); 162fff69388SHerbert Xu 163fff69388SHerbert Xu x->u.rt.idev = in_dev_get(rt->u.dst.dev); 164fff69388SHerbert Xu if (!x->u.rt.idev) 165fff69388SHerbert Xu goto error; 166fff69388SHerbert Xu 1671da177e4SLinus Torvalds dst_prev->obsolete = -1; 1681da177e4SLinus Torvalds dst_prev->flags |= DST_HOST; 1691da177e4SLinus Torvalds dst_prev->lastuse = jiffies; 1701da177e4SLinus Torvalds dst_prev->header_len = header_len; 1711da177e4SLinus Torvalds dst_prev->trailer_len = trailer_len; 1721da177e4SLinus Torvalds memcpy(&dst_prev->metrics, &x->route->metrics, sizeof(dst_prev->metrics)); 1731da177e4SLinus Torvalds 17445ff5a3fSHerbert Xu dst_prev->input = dst_discard; 17513996378SHerbert Xu dst_prev->output = dst_prev->xfrm->outer_mode->afinfo->output; 176ed3e37ddSHerbert Xu if (rt0->peer) 177ed3e37ddSHerbert Xu atomic_inc(&rt0->peer->refcnt); 178ed3e37ddSHerbert Xu x->u.rt.peer = rt0->peer; 1791da177e4SLinus Torvalds /* Sheit... I remember I did this right. Apparently, 1801da177e4SLinus Torvalds * it was magically lost, so this code needs audit */ 1811da177e4SLinus Torvalds x->u.rt.rt_flags = rt0->rt_flags&(RTCF_BROADCAST|RTCF_MULTICAST|RTCF_LOCAL); 182ed3e37ddSHerbert Xu x->u.rt.rt_type = rt0->rt_type; 1831da177e4SLinus Torvalds x->u.rt.rt_src = rt0->rt_src; 1841da177e4SLinus Torvalds x->u.rt.rt_dst = rt0->rt_dst; 185ed3e37ddSHerbert Xu x->u.rt.rt_gateway = rt0->rt_gateway; 1861da177e4SLinus Torvalds x->u.rt.rt_spec_dst = rt0->rt_spec_dst; 1871da177e4SLinus Torvalds header_len -= x->u.dst.xfrm->props.header_len; 1881da177e4SLinus Torvalds trailer_len -= x->u.dst.xfrm->props.trailer_len; 1891da177e4SLinus Torvalds } 1901da177e4SLinus Torvalds 1911da177e4SLinus Torvalds xfrm_init_pmtu(dst); 1921da177e4SLinus Torvalds return 0; 1931da177e4SLinus Torvalds 1941da177e4SLinus Torvalds error: 1951da177e4SLinus Torvalds if (dst) 1961da177e4SLinus Torvalds dst_free(dst); 1971da177e4SLinus Torvalds return err; 1981da177e4SLinus Torvalds } 1991da177e4SLinus Torvalds 2001da177e4SLinus Torvalds static void 2011da177e4SLinus Torvalds _decode_session4(struct sk_buff *skb, struct flowi *fl) 2021da177e4SLinus Torvalds { 203eddc9ec5SArnaldo Carvalho de Melo struct iphdr *iph = ip_hdr(skb); 204d56f90a7SArnaldo Carvalho de Melo u8 *xprth = skb_network_header(skb) + iph->ihl * 4; 2051da177e4SLinus Torvalds 2061da177e4SLinus Torvalds memset(fl, 0, sizeof(struct flowi)); 2071da177e4SLinus Torvalds if (!(iph->frag_off & htons(IP_MF | IP_OFFSET))) { 2081da177e4SLinus Torvalds switch (iph->protocol) { 2091da177e4SLinus Torvalds case IPPROTO_UDP: 210ba4e58ecSGerrit Renker case IPPROTO_UDPLITE: 2111da177e4SLinus Torvalds case IPPROTO_TCP: 2121da177e4SLinus Torvalds case IPPROTO_SCTP: 2139e999993SPatrick McHardy case IPPROTO_DCCP: 2141da177e4SLinus Torvalds if (pskb_may_pull(skb, xprth + 4 - skb->data)) { 2158c689a6eSAl Viro __be16 *ports = (__be16 *)xprth; 2161da177e4SLinus Torvalds 2171da177e4SLinus Torvalds fl->fl_ip_sport = ports[0]; 2181da177e4SLinus Torvalds fl->fl_ip_dport = ports[1]; 2191da177e4SLinus Torvalds } 2201da177e4SLinus Torvalds break; 2211da177e4SLinus Torvalds 2221da177e4SLinus Torvalds case IPPROTO_ICMP: 2231da177e4SLinus Torvalds if (pskb_may_pull(skb, xprth + 2 - skb->data)) { 2241da177e4SLinus Torvalds u8 *icmp = xprth; 2251da177e4SLinus Torvalds 2261da177e4SLinus Torvalds fl->fl_icmp_type = icmp[0]; 2271da177e4SLinus Torvalds fl->fl_icmp_code = icmp[1]; 2281da177e4SLinus Torvalds } 2291da177e4SLinus Torvalds break; 2301da177e4SLinus Torvalds 2311da177e4SLinus Torvalds case IPPROTO_ESP: 2321da177e4SLinus Torvalds if (pskb_may_pull(skb, xprth + 4 - skb->data)) { 2334324a174SAl Viro __be32 *ehdr = (__be32 *)xprth; 2341da177e4SLinus Torvalds 2351da177e4SLinus Torvalds fl->fl_ipsec_spi = ehdr[0]; 2361da177e4SLinus Torvalds } 2371da177e4SLinus Torvalds break; 2381da177e4SLinus Torvalds 2391da177e4SLinus Torvalds case IPPROTO_AH: 2401da177e4SLinus Torvalds if (pskb_may_pull(skb, xprth + 8 - skb->data)) { 2414324a174SAl Viro __be32 *ah_hdr = (__be32*)xprth; 2421da177e4SLinus Torvalds 2431da177e4SLinus Torvalds fl->fl_ipsec_spi = ah_hdr[1]; 2441da177e4SLinus Torvalds } 2451da177e4SLinus Torvalds break; 2461da177e4SLinus Torvalds 2471da177e4SLinus Torvalds case IPPROTO_COMP: 2481da177e4SLinus Torvalds if (pskb_may_pull(skb, xprth + 4 - skb->data)) { 2494324a174SAl Viro __be16 *ipcomp_hdr = (__be16 *)xprth; 2501da177e4SLinus Torvalds 2514195f814SAlexey Dobriyan fl->fl_ipsec_spi = htonl(ntohs(ipcomp_hdr[1])); 2521da177e4SLinus Torvalds } 2531da177e4SLinus Torvalds break; 2541da177e4SLinus Torvalds default: 2551da177e4SLinus Torvalds fl->fl_ipsec_spi = 0; 2561da177e4SLinus Torvalds break; 2573ff50b79SStephen Hemminger } 2581da177e4SLinus Torvalds } 2591da177e4SLinus Torvalds fl->proto = iph->protocol; 2601da177e4SLinus Torvalds fl->fl4_dst = iph->daddr; 2611da177e4SLinus Torvalds fl->fl4_src = iph->saddr; 2624da3089fSHerbert Xu fl->fl4_tos = iph->tos; 2631da177e4SLinus Torvalds } 2641da177e4SLinus Torvalds 2651da177e4SLinus Torvalds static inline int xfrm4_garbage_collect(void) 2661da177e4SLinus Torvalds { 2671da177e4SLinus Torvalds xfrm4_policy_afinfo.garbage_collect(); 2681da177e4SLinus Torvalds return (atomic_read(&xfrm4_dst_ops.entries) > xfrm4_dst_ops.gc_thresh*2); 2691da177e4SLinus Torvalds } 2701da177e4SLinus Torvalds 2711da177e4SLinus Torvalds static void xfrm4_update_pmtu(struct dst_entry *dst, u32 mtu) 2721da177e4SLinus Torvalds { 2731da177e4SLinus Torvalds struct xfrm_dst *xdst = (struct xfrm_dst *)dst; 2741da177e4SLinus Torvalds struct dst_entry *path = xdst->route; 2751da177e4SLinus Torvalds 2761da177e4SLinus Torvalds path->ops->update_pmtu(path, mtu); 2771da177e4SLinus Torvalds } 2781da177e4SLinus Torvalds 279aabc9761SHerbert Xu static void xfrm4_dst_destroy(struct dst_entry *dst) 280aabc9761SHerbert Xu { 281aabc9761SHerbert Xu struct xfrm_dst *xdst = (struct xfrm_dst *)dst; 282aabc9761SHerbert Xu 283aabc9761SHerbert Xu if (likely(xdst->u.rt.idev)) 284aabc9761SHerbert Xu in_dev_put(xdst->u.rt.idev); 285ed3e37ddSHerbert Xu if (likely(xdst->u.rt.peer)) 28626db1677SDavid S. Miller inet_putpeer(xdst->u.rt.peer); 287aabc9761SHerbert Xu xfrm_dst_destroy(xdst); 288aabc9761SHerbert Xu } 289aabc9761SHerbert Xu 290aabc9761SHerbert Xu static void xfrm4_dst_ifdown(struct dst_entry *dst, struct net_device *dev, 291aabc9761SHerbert Xu int unregister) 292aabc9761SHerbert Xu { 293aabc9761SHerbert Xu struct xfrm_dst *xdst; 294aabc9761SHerbert Xu 295aabc9761SHerbert Xu if (!unregister) 296aabc9761SHerbert Xu return; 297aabc9761SHerbert Xu 298aabc9761SHerbert Xu xdst = (struct xfrm_dst *)dst; 299aabc9761SHerbert Xu if (xdst->u.rt.idev->dev == dev) { 3002774c7abSEric W. Biederman struct in_device *loopback_idev = in_dev_get(init_net.loopback_dev); 301aabc9761SHerbert Xu BUG_ON(!loopback_idev); 302aabc9761SHerbert Xu 303aabc9761SHerbert Xu do { 304aabc9761SHerbert Xu in_dev_put(xdst->u.rt.idev); 305aabc9761SHerbert Xu xdst->u.rt.idev = loopback_idev; 306aabc9761SHerbert Xu in_dev_hold(loopback_idev); 307aabc9761SHerbert Xu xdst = (struct xfrm_dst *)xdst->u.dst.child; 308aabc9761SHerbert Xu } while (xdst->u.dst.xfrm); 309aabc9761SHerbert Xu 310aabc9761SHerbert Xu __in_dev_put(loopback_idev); 311aabc9761SHerbert Xu } 312aabc9761SHerbert Xu 313aabc9761SHerbert Xu xfrm_dst_ifdown(dst, dev); 314aabc9761SHerbert Xu } 315aabc9761SHerbert Xu 3161da177e4SLinus Torvalds static struct dst_ops xfrm4_dst_ops = { 3171da177e4SLinus Torvalds .family = AF_INET, 3181da177e4SLinus Torvalds .protocol = __constant_htons(ETH_P_IP), 3191da177e4SLinus Torvalds .gc = xfrm4_garbage_collect, 3201da177e4SLinus Torvalds .update_pmtu = xfrm4_update_pmtu, 321aabc9761SHerbert Xu .destroy = xfrm4_dst_destroy, 322aabc9761SHerbert Xu .ifdown = xfrm4_dst_ifdown, 3231da177e4SLinus Torvalds .gc_thresh = 1024, 3241da177e4SLinus Torvalds .entry_size = sizeof(struct xfrm_dst), 3251da177e4SLinus Torvalds }; 3261da177e4SLinus Torvalds 3271da177e4SLinus Torvalds static struct xfrm_policy_afinfo xfrm4_policy_afinfo = { 3281da177e4SLinus Torvalds .family = AF_INET, 3291da177e4SLinus Torvalds .dst_ops = &xfrm4_dst_ops, 3301da177e4SLinus Torvalds .dst_lookup = xfrm4_dst_lookup, 331a1e59abfSPatrick McHardy .get_saddr = xfrm4_get_saddr, 3321da177e4SLinus Torvalds .find_bundle = __xfrm4_find_bundle, 3331da177e4SLinus Torvalds .bundle_create = __xfrm4_bundle_create, 3341da177e4SLinus Torvalds .decode_session = _decode_session4, 3351da177e4SLinus Torvalds }; 3361da177e4SLinus Torvalds 3371da177e4SLinus Torvalds static void __init xfrm4_policy_init(void) 3381da177e4SLinus Torvalds { 3391da177e4SLinus Torvalds xfrm_policy_register_afinfo(&xfrm4_policy_afinfo); 3401da177e4SLinus Torvalds } 3411da177e4SLinus Torvalds 3421da177e4SLinus Torvalds static void __exit xfrm4_policy_fini(void) 3431da177e4SLinus Torvalds { 3441da177e4SLinus Torvalds xfrm_policy_unregister_afinfo(&xfrm4_policy_afinfo); 3451da177e4SLinus Torvalds } 3461da177e4SLinus Torvalds 3471da177e4SLinus Torvalds void __init xfrm4_init(void) 3481da177e4SLinus Torvalds { 3491da177e4SLinus Torvalds xfrm4_state_init(); 3501da177e4SLinus Torvalds xfrm4_policy_init(); 3511da177e4SLinus Torvalds } 3521da177e4SLinus Torvalds 353