1*d1df6fd8SDavid Lebrun /* 2*d1df6fd8SDavid Lebrun * SR-IPv6 implementation 3*d1df6fd8SDavid Lebrun * 4*d1df6fd8SDavid Lebrun * Author: 5*d1df6fd8SDavid Lebrun * David Lebrun <david.lebrun@uclouvain.be> 6*d1df6fd8SDavid Lebrun * 7*d1df6fd8SDavid Lebrun * 8*d1df6fd8SDavid Lebrun * This program is free software; you can redistribute it and/or 9*d1df6fd8SDavid Lebrun * modify it under the terms of the GNU General Public License 10*d1df6fd8SDavid Lebrun * as published by the Free Software Foundation; either version 11*d1df6fd8SDavid Lebrun * 2 of the License, or (at your option) any later version. 12*d1df6fd8SDavid Lebrun */ 13*d1df6fd8SDavid Lebrun 14*d1df6fd8SDavid Lebrun #include <linux/types.h> 15*d1df6fd8SDavid Lebrun #include <linux/skbuff.h> 16*d1df6fd8SDavid Lebrun #include <linux/net.h> 17*d1df6fd8SDavid Lebrun #include <linux/module.h> 18*d1df6fd8SDavid Lebrun #include <net/ip.h> 19*d1df6fd8SDavid Lebrun #include <net/lwtunnel.h> 20*d1df6fd8SDavid Lebrun #include <net/netevent.h> 21*d1df6fd8SDavid Lebrun #include <net/netns/generic.h> 22*d1df6fd8SDavid Lebrun #include <net/ip6_fib.h> 23*d1df6fd8SDavid Lebrun #include <net/route.h> 24*d1df6fd8SDavid Lebrun #include <net/seg6.h> 25*d1df6fd8SDavid Lebrun #include <linux/seg6.h> 26*d1df6fd8SDavid Lebrun #include <linux/seg6_local.h> 27*d1df6fd8SDavid Lebrun #include <net/addrconf.h> 28*d1df6fd8SDavid Lebrun #include <net/ip6_route.h> 29*d1df6fd8SDavid Lebrun #include <net/dst_cache.h> 30*d1df6fd8SDavid Lebrun #ifdef CONFIG_IPV6_SEG6_HMAC 31*d1df6fd8SDavid Lebrun #include <net/seg6_hmac.h> 32*d1df6fd8SDavid Lebrun #endif 33*d1df6fd8SDavid Lebrun 34*d1df6fd8SDavid Lebrun struct seg6_local_lwt; 35*d1df6fd8SDavid Lebrun 36*d1df6fd8SDavid Lebrun struct seg6_action_desc { 37*d1df6fd8SDavid Lebrun int action; 38*d1df6fd8SDavid Lebrun unsigned long attrs; 39*d1df6fd8SDavid Lebrun int (*input)(struct sk_buff *skb, struct seg6_local_lwt *slwt); 40*d1df6fd8SDavid Lebrun int static_headroom; 41*d1df6fd8SDavid Lebrun }; 42*d1df6fd8SDavid Lebrun 43*d1df6fd8SDavid Lebrun struct seg6_local_lwt { 44*d1df6fd8SDavid Lebrun int action; 45*d1df6fd8SDavid Lebrun struct ipv6_sr_hdr *srh; 46*d1df6fd8SDavid Lebrun int table; 47*d1df6fd8SDavid Lebrun struct in_addr nh4; 48*d1df6fd8SDavid Lebrun struct in6_addr nh6; 49*d1df6fd8SDavid Lebrun int iif; 50*d1df6fd8SDavid Lebrun int oif; 51*d1df6fd8SDavid Lebrun 52*d1df6fd8SDavid Lebrun int headroom; 53*d1df6fd8SDavid Lebrun struct seg6_action_desc *desc; 54*d1df6fd8SDavid Lebrun }; 55*d1df6fd8SDavid Lebrun 56*d1df6fd8SDavid Lebrun static struct seg6_local_lwt *seg6_local_lwtunnel(struct lwtunnel_state *lwt) 57*d1df6fd8SDavid Lebrun { 58*d1df6fd8SDavid Lebrun return (struct seg6_local_lwt *)lwt->data; 59*d1df6fd8SDavid Lebrun } 60*d1df6fd8SDavid Lebrun 61*d1df6fd8SDavid Lebrun static struct seg6_action_desc seg6_action_table[] = { 62*d1df6fd8SDavid Lebrun { 63*d1df6fd8SDavid Lebrun .action = SEG6_LOCAL_ACTION_END, 64*d1df6fd8SDavid Lebrun .attrs = 0, 65*d1df6fd8SDavid Lebrun }, 66*d1df6fd8SDavid Lebrun }; 67*d1df6fd8SDavid Lebrun 68*d1df6fd8SDavid Lebrun static struct seg6_action_desc *__get_action_desc(int action) 69*d1df6fd8SDavid Lebrun { 70*d1df6fd8SDavid Lebrun struct seg6_action_desc *desc; 71*d1df6fd8SDavid Lebrun int i, count; 72*d1df6fd8SDavid Lebrun 73*d1df6fd8SDavid Lebrun count = sizeof(seg6_action_table) / sizeof(struct seg6_action_desc); 74*d1df6fd8SDavid Lebrun for (i = 0; i < count; i++) { 75*d1df6fd8SDavid Lebrun desc = &seg6_action_table[i]; 76*d1df6fd8SDavid Lebrun if (desc->action == action) 77*d1df6fd8SDavid Lebrun return desc; 78*d1df6fd8SDavid Lebrun } 79*d1df6fd8SDavid Lebrun 80*d1df6fd8SDavid Lebrun return NULL; 81*d1df6fd8SDavid Lebrun } 82*d1df6fd8SDavid Lebrun 83*d1df6fd8SDavid Lebrun static int seg6_local_input(struct sk_buff *skb) 84*d1df6fd8SDavid Lebrun { 85*d1df6fd8SDavid Lebrun struct dst_entry *orig_dst = skb_dst(skb); 86*d1df6fd8SDavid Lebrun struct seg6_action_desc *desc; 87*d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt; 88*d1df6fd8SDavid Lebrun 89*d1df6fd8SDavid Lebrun slwt = seg6_local_lwtunnel(orig_dst->lwtstate); 90*d1df6fd8SDavid Lebrun desc = slwt->desc; 91*d1df6fd8SDavid Lebrun 92*d1df6fd8SDavid Lebrun return desc->input(skb, slwt); 93*d1df6fd8SDavid Lebrun } 94*d1df6fd8SDavid Lebrun 95*d1df6fd8SDavid Lebrun static const struct nla_policy seg6_local_policy[SEG6_LOCAL_MAX + 1] = { 96*d1df6fd8SDavid Lebrun [SEG6_LOCAL_ACTION] = { .type = NLA_U32 }, 97*d1df6fd8SDavid Lebrun [SEG6_LOCAL_SRH] = { .type = NLA_BINARY }, 98*d1df6fd8SDavid Lebrun [SEG6_LOCAL_TABLE] = { .type = NLA_U32 }, 99*d1df6fd8SDavid Lebrun [SEG6_LOCAL_NH4] = { .type = NLA_BINARY, 100*d1df6fd8SDavid Lebrun .len = sizeof(struct in_addr) }, 101*d1df6fd8SDavid Lebrun [SEG6_LOCAL_NH6] = { .type = NLA_BINARY, 102*d1df6fd8SDavid Lebrun .len = sizeof(struct in6_addr) }, 103*d1df6fd8SDavid Lebrun [SEG6_LOCAL_IIF] = { .type = NLA_U32 }, 104*d1df6fd8SDavid Lebrun [SEG6_LOCAL_OIF] = { .type = NLA_U32 }, 105*d1df6fd8SDavid Lebrun }; 106*d1df6fd8SDavid Lebrun 107*d1df6fd8SDavid Lebrun struct seg6_action_param { 108*d1df6fd8SDavid Lebrun int (*parse)(struct nlattr **attrs, struct seg6_local_lwt *slwt); 109*d1df6fd8SDavid Lebrun int (*put)(struct sk_buff *skb, struct seg6_local_lwt *slwt); 110*d1df6fd8SDavid Lebrun int (*cmp)(struct seg6_local_lwt *a, struct seg6_local_lwt *b); 111*d1df6fd8SDavid Lebrun }; 112*d1df6fd8SDavid Lebrun 113*d1df6fd8SDavid Lebrun static struct seg6_action_param seg6_action_params[SEG6_LOCAL_MAX + 1] = { 114*d1df6fd8SDavid Lebrun [SEG6_LOCAL_SRH] = { .parse = NULL, 115*d1df6fd8SDavid Lebrun .put = NULL, 116*d1df6fd8SDavid Lebrun .cmp = NULL }, 117*d1df6fd8SDavid Lebrun 118*d1df6fd8SDavid Lebrun [SEG6_LOCAL_TABLE] = { .parse = NULL, 119*d1df6fd8SDavid Lebrun .put = NULL, 120*d1df6fd8SDavid Lebrun .cmp = NULL }, 121*d1df6fd8SDavid Lebrun 122*d1df6fd8SDavid Lebrun [SEG6_LOCAL_NH4] = { .parse = NULL, 123*d1df6fd8SDavid Lebrun .put = NULL, 124*d1df6fd8SDavid Lebrun .cmp = NULL }, 125*d1df6fd8SDavid Lebrun 126*d1df6fd8SDavid Lebrun [SEG6_LOCAL_NH6] = { .parse = NULL, 127*d1df6fd8SDavid Lebrun .put = NULL, 128*d1df6fd8SDavid Lebrun .cmp = NULL }, 129*d1df6fd8SDavid Lebrun 130*d1df6fd8SDavid Lebrun [SEG6_LOCAL_IIF] = { .parse = NULL, 131*d1df6fd8SDavid Lebrun .put = NULL, 132*d1df6fd8SDavid Lebrun .cmp = NULL }, 133*d1df6fd8SDavid Lebrun 134*d1df6fd8SDavid Lebrun [SEG6_LOCAL_OIF] = { .parse = NULL, 135*d1df6fd8SDavid Lebrun .put = NULL, 136*d1df6fd8SDavid Lebrun .cmp = NULL }, 137*d1df6fd8SDavid Lebrun }; 138*d1df6fd8SDavid Lebrun 139*d1df6fd8SDavid Lebrun static int parse_nla_action(struct nlattr **attrs, struct seg6_local_lwt *slwt) 140*d1df6fd8SDavid Lebrun { 141*d1df6fd8SDavid Lebrun struct seg6_action_param *param; 142*d1df6fd8SDavid Lebrun struct seg6_action_desc *desc; 143*d1df6fd8SDavid Lebrun int i, err; 144*d1df6fd8SDavid Lebrun 145*d1df6fd8SDavid Lebrun desc = __get_action_desc(slwt->action); 146*d1df6fd8SDavid Lebrun if (!desc) 147*d1df6fd8SDavid Lebrun return -EINVAL; 148*d1df6fd8SDavid Lebrun 149*d1df6fd8SDavid Lebrun if (!desc->input) 150*d1df6fd8SDavid Lebrun return -EOPNOTSUPP; 151*d1df6fd8SDavid Lebrun 152*d1df6fd8SDavid Lebrun slwt->desc = desc; 153*d1df6fd8SDavid Lebrun slwt->headroom += desc->static_headroom; 154*d1df6fd8SDavid Lebrun 155*d1df6fd8SDavid Lebrun for (i = 0; i < SEG6_LOCAL_MAX + 1; i++) { 156*d1df6fd8SDavid Lebrun if (desc->attrs & (1 << i)) { 157*d1df6fd8SDavid Lebrun if (!attrs[i]) 158*d1df6fd8SDavid Lebrun return -EINVAL; 159*d1df6fd8SDavid Lebrun 160*d1df6fd8SDavid Lebrun param = &seg6_action_params[i]; 161*d1df6fd8SDavid Lebrun 162*d1df6fd8SDavid Lebrun err = param->parse(attrs, slwt); 163*d1df6fd8SDavid Lebrun if (err < 0) 164*d1df6fd8SDavid Lebrun return err; 165*d1df6fd8SDavid Lebrun } 166*d1df6fd8SDavid Lebrun } 167*d1df6fd8SDavid Lebrun 168*d1df6fd8SDavid Lebrun return 0; 169*d1df6fd8SDavid Lebrun } 170*d1df6fd8SDavid Lebrun 171*d1df6fd8SDavid Lebrun static int seg6_local_build_state(struct nlattr *nla, unsigned int family, 172*d1df6fd8SDavid Lebrun const void *cfg, struct lwtunnel_state **ts, 173*d1df6fd8SDavid Lebrun struct netlink_ext_ack *extack) 174*d1df6fd8SDavid Lebrun { 175*d1df6fd8SDavid Lebrun struct nlattr *tb[SEG6_LOCAL_MAX + 1]; 176*d1df6fd8SDavid Lebrun struct lwtunnel_state *newts; 177*d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt; 178*d1df6fd8SDavid Lebrun int err; 179*d1df6fd8SDavid Lebrun 180*d1df6fd8SDavid Lebrun err = nla_parse_nested(tb, SEG6_LOCAL_MAX, nla, seg6_local_policy, 181*d1df6fd8SDavid Lebrun extack); 182*d1df6fd8SDavid Lebrun 183*d1df6fd8SDavid Lebrun if (err < 0) 184*d1df6fd8SDavid Lebrun return err; 185*d1df6fd8SDavid Lebrun 186*d1df6fd8SDavid Lebrun if (!tb[SEG6_LOCAL_ACTION]) 187*d1df6fd8SDavid Lebrun return -EINVAL; 188*d1df6fd8SDavid Lebrun 189*d1df6fd8SDavid Lebrun newts = lwtunnel_state_alloc(sizeof(*slwt)); 190*d1df6fd8SDavid Lebrun if (!newts) 191*d1df6fd8SDavid Lebrun return -ENOMEM; 192*d1df6fd8SDavid Lebrun 193*d1df6fd8SDavid Lebrun slwt = seg6_local_lwtunnel(newts); 194*d1df6fd8SDavid Lebrun slwt->action = nla_get_u32(tb[SEG6_LOCAL_ACTION]); 195*d1df6fd8SDavid Lebrun 196*d1df6fd8SDavid Lebrun err = parse_nla_action(tb, slwt); 197*d1df6fd8SDavid Lebrun if (err < 0) 198*d1df6fd8SDavid Lebrun goto out_free; 199*d1df6fd8SDavid Lebrun 200*d1df6fd8SDavid Lebrun newts->type = LWTUNNEL_ENCAP_SEG6_LOCAL; 201*d1df6fd8SDavid Lebrun newts->flags = LWTUNNEL_STATE_INPUT_REDIRECT; 202*d1df6fd8SDavid Lebrun newts->headroom = slwt->headroom; 203*d1df6fd8SDavid Lebrun 204*d1df6fd8SDavid Lebrun *ts = newts; 205*d1df6fd8SDavid Lebrun 206*d1df6fd8SDavid Lebrun return 0; 207*d1df6fd8SDavid Lebrun 208*d1df6fd8SDavid Lebrun out_free: 209*d1df6fd8SDavid Lebrun kfree(slwt->srh); 210*d1df6fd8SDavid Lebrun kfree(newts); 211*d1df6fd8SDavid Lebrun return err; 212*d1df6fd8SDavid Lebrun } 213*d1df6fd8SDavid Lebrun 214*d1df6fd8SDavid Lebrun static void seg6_local_destroy_state(struct lwtunnel_state *lwt) 215*d1df6fd8SDavid Lebrun { 216*d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt = seg6_local_lwtunnel(lwt); 217*d1df6fd8SDavid Lebrun 218*d1df6fd8SDavid Lebrun kfree(slwt->srh); 219*d1df6fd8SDavid Lebrun } 220*d1df6fd8SDavid Lebrun 221*d1df6fd8SDavid Lebrun static int seg6_local_fill_encap(struct sk_buff *skb, 222*d1df6fd8SDavid Lebrun struct lwtunnel_state *lwt) 223*d1df6fd8SDavid Lebrun { 224*d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt = seg6_local_lwtunnel(lwt); 225*d1df6fd8SDavid Lebrun struct seg6_action_param *param; 226*d1df6fd8SDavid Lebrun int i, err; 227*d1df6fd8SDavid Lebrun 228*d1df6fd8SDavid Lebrun if (nla_put_u32(skb, SEG6_LOCAL_ACTION, slwt->action)) 229*d1df6fd8SDavid Lebrun return -EMSGSIZE; 230*d1df6fd8SDavid Lebrun 231*d1df6fd8SDavid Lebrun for (i = 0; i < SEG6_LOCAL_MAX + 1; i++) { 232*d1df6fd8SDavid Lebrun if (slwt->desc->attrs & (1 << i)) { 233*d1df6fd8SDavid Lebrun param = &seg6_action_params[i]; 234*d1df6fd8SDavid Lebrun err = param->put(skb, slwt); 235*d1df6fd8SDavid Lebrun if (err < 0) 236*d1df6fd8SDavid Lebrun return err; 237*d1df6fd8SDavid Lebrun } 238*d1df6fd8SDavid Lebrun } 239*d1df6fd8SDavid Lebrun 240*d1df6fd8SDavid Lebrun return 0; 241*d1df6fd8SDavid Lebrun } 242*d1df6fd8SDavid Lebrun 243*d1df6fd8SDavid Lebrun static int seg6_local_get_encap_size(struct lwtunnel_state *lwt) 244*d1df6fd8SDavid Lebrun { 245*d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt = seg6_local_lwtunnel(lwt); 246*d1df6fd8SDavid Lebrun unsigned long attrs; 247*d1df6fd8SDavid Lebrun int nlsize; 248*d1df6fd8SDavid Lebrun 249*d1df6fd8SDavid Lebrun nlsize = nla_total_size(4); /* action */ 250*d1df6fd8SDavid Lebrun 251*d1df6fd8SDavid Lebrun attrs = slwt->desc->attrs; 252*d1df6fd8SDavid Lebrun 253*d1df6fd8SDavid Lebrun if (attrs & (1 << SEG6_LOCAL_SRH)) 254*d1df6fd8SDavid Lebrun nlsize += nla_total_size((slwt->srh->hdrlen + 1) << 3); 255*d1df6fd8SDavid Lebrun 256*d1df6fd8SDavid Lebrun if (attrs & (1 << SEG6_LOCAL_TABLE)) 257*d1df6fd8SDavid Lebrun nlsize += nla_total_size(4); 258*d1df6fd8SDavid Lebrun 259*d1df6fd8SDavid Lebrun if (attrs & (1 << SEG6_LOCAL_NH4)) 260*d1df6fd8SDavid Lebrun nlsize += nla_total_size(4); 261*d1df6fd8SDavid Lebrun 262*d1df6fd8SDavid Lebrun if (attrs & (1 << SEG6_LOCAL_NH6)) 263*d1df6fd8SDavid Lebrun nlsize += nla_total_size(16); 264*d1df6fd8SDavid Lebrun 265*d1df6fd8SDavid Lebrun if (attrs & (1 << SEG6_LOCAL_IIF)) 266*d1df6fd8SDavid Lebrun nlsize += nla_total_size(4); 267*d1df6fd8SDavid Lebrun 268*d1df6fd8SDavid Lebrun if (attrs & (1 << SEG6_LOCAL_OIF)) 269*d1df6fd8SDavid Lebrun nlsize += nla_total_size(4); 270*d1df6fd8SDavid Lebrun 271*d1df6fd8SDavid Lebrun return nlsize; 272*d1df6fd8SDavid Lebrun } 273*d1df6fd8SDavid Lebrun 274*d1df6fd8SDavid Lebrun static int seg6_local_cmp_encap(struct lwtunnel_state *a, 275*d1df6fd8SDavid Lebrun struct lwtunnel_state *b) 276*d1df6fd8SDavid Lebrun { 277*d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt_a, *slwt_b; 278*d1df6fd8SDavid Lebrun struct seg6_action_param *param; 279*d1df6fd8SDavid Lebrun int i; 280*d1df6fd8SDavid Lebrun 281*d1df6fd8SDavid Lebrun slwt_a = seg6_local_lwtunnel(a); 282*d1df6fd8SDavid Lebrun slwt_b = seg6_local_lwtunnel(b); 283*d1df6fd8SDavid Lebrun 284*d1df6fd8SDavid Lebrun if (slwt_a->action != slwt_b->action) 285*d1df6fd8SDavid Lebrun return 1; 286*d1df6fd8SDavid Lebrun 287*d1df6fd8SDavid Lebrun if (slwt_a->desc->attrs != slwt_b->desc->attrs) 288*d1df6fd8SDavid Lebrun return 1; 289*d1df6fd8SDavid Lebrun 290*d1df6fd8SDavid Lebrun for (i = 0; i < SEG6_LOCAL_MAX + 1; i++) { 291*d1df6fd8SDavid Lebrun if (slwt_a->desc->attrs & (1 << i)) { 292*d1df6fd8SDavid Lebrun param = &seg6_action_params[i]; 293*d1df6fd8SDavid Lebrun if (param->cmp(slwt_a, slwt_b)) 294*d1df6fd8SDavid Lebrun return 1; 295*d1df6fd8SDavid Lebrun } 296*d1df6fd8SDavid Lebrun } 297*d1df6fd8SDavid Lebrun 298*d1df6fd8SDavid Lebrun return 0; 299*d1df6fd8SDavid Lebrun } 300*d1df6fd8SDavid Lebrun 301*d1df6fd8SDavid Lebrun static const struct lwtunnel_encap_ops seg6_local_ops = { 302*d1df6fd8SDavid Lebrun .build_state = seg6_local_build_state, 303*d1df6fd8SDavid Lebrun .destroy_state = seg6_local_destroy_state, 304*d1df6fd8SDavid Lebrun .input = seg6_local_input, 305*d1df6fd8SDavid Lebrun .fill_encap = seg6_local_fill_encap, 306*d1df6fd8SDavid Lebrun .get_encap_size = seg6_local_get_encap_size, 307*d1df6fd8SDavid Lebrun .cmp_encap = seg6_local_cmp_encap, 308*d1df6fd8SDavid Lebrun .owner = THIS_MODULE, 309*d1df6fd8SDavid Lebrun }; 310*d1df6fd8SDavid Lebrun 311*d1df6fd8SDavid Lebrun int __init seg6_local_init(void) 312*d1df6fd8SDavid Lebrun { 313*d1df6fd8SDavid Lebrun return lwtunnel_encap_add_ops(&seg6_local_ops, 314*d1df6fd8SDavid Lebrun LWTUNNEL_ENCAP_SEG6_LOCAL); 315*d1df6fd8SDavid Lebrun } 316*d1df6fd8SDavid Lebrun 317*d1df6fd8SDavid Lebrun void seg6_local_exit(void) 318*d1df6fd8SDavid Lebrun { 319*d1df6fd8SDavid Lebrun lwtunnel_encap_del_ops(&seg6_local_ops, LWTUNNEL_ENCAP_SEG6_LOCAL); 320*d1df6fd8SDavid Lebrun } 321