1 #include <linux/errno.h> 2 #include <linux/ip.h> 3 #include <linux/kernel.h> 4 #include <linux/module.h> 5 #include <linux/skbuff.h> 6 #include <linux/socket.h> 7 #include <linux/types.h> 8 #include <net/checksum.h> 9 #include <net/ip.h> 10 #include <net/ip6_fib.h> 11 #include <net/lwtunnel.h> 12 #include <net/protocol.h> 13 #include <uapi/linux/ila.h> 14 #include "ila.h" 15 16 static inline struct ila_params *ila_params_lwtunnel( 17 struct lwtunnel_state *lwstate) 18 { 19 return (struct ila_params *)lwstate->data; 20 } 21 22 static int ila_output(struct net *net, struct sock *sk, struct sk_buff *skb) 23 { 24 struct dst_entry *dst = skb_dst(skb); 25 26 if (skb->protocol != htons(ETH_P_IPV6)) 27 goto drop; 28 29 update_ipv6_locator(skb, ila_params_lwtunnel(dst->lwtstate)); 30 31 return dst->lwtstate->orig_output(net, sk, skb); 32 33 drop: 34 kfree_skb(skb); 35 return -EINVAL; 36 } 37 38 static int ila_input(struct sk_buff *skb) 39 { 40 struct dst_entry *dst = skb_dst(skb); 41 42 if (skb->protocol != htons(ETH_P_IPV6)) 43 goto drop; 44 45 update_ipv6_locator(skb, ila_params_lwtunnel(dst->lwtstate)); 46 47 return dst->lwtstate->orig_input(skb); 48 49 drop: 50 kfree_skb(skb); 51 return -EINVAL; 52 } 53 54 static struct nla_policy ila_nl_policy[ILA_ATTR_MAX + 1] = { 55 [ILA_ATTR_LOCATOR] = { .type = NLA_U64, }, 56 }; 57 58 static int ila_build_state(struct net_device *dev, struct nlattr *nla, 59 unsigned int family, const void *cfg, 60 struct lwtunnel_state **ts) 61 { 62 struct ila_params *p; 63 struct nlattr *tb[ILA_ATTR_MAX + 1]; 64 size_t encap_len = sizeof(*p); 65 struct lwtunnel_state *newts; 66 const struct fib6_config *cfg6 = cfg; 67 int ret; 68 69 if (family != AF_INET6) 70 return -EINVAL; 71 72 ret = nla_parse_nested(tb, ILA_ATTR_MAX, nla, 73 ila_nl_policy); 74 if (ret < 0) 75 return ret; 76 77 if (!tb[ILA_ATTR_LOCATOR]) 78 return -EINVAL; 79 80 newts = lwtunnel_state_alloc(encap_len); 81 if (!newts) 82 return -ENOMEM; 83 84 newts->len = encap_len; 85 p = ila_params_lwtunnel(newts); 86 87 p->locator = (__force __be64)nla_get_u64(tb[ILA_ATTR_LOCATOR]); 88 89 if (cfg6->fc_dst_len > sizeof(__be64)) { 90 /* Precompute checksum difference for translation since we 91 * know both the old locator and the new one. 92 */ 93 p->locator_match = *(__be64 *)&cfg6->fc_dst; 94 p->csum_diff = compute_csum_diff8( 95 (__be32 *)&p->locator_match, (__be32 *)&p->locator); 96 } 97 98 newts->type = LWTUNNEL_ENCAP_ILA; 99 newts->flags |= LWTUNNEL_STATE_OUTPUT_REDIRECT | 100 LWTUNNEL_STATE_INPUT_REDIRECT; 101 102 *ts = newts; 103 104 return 0; 105 } 106 107 static int ila_fill_encap_info(struct sk_buff *skb, 108 struct lwtunnel_state *lwtstate) 109 { 110 struct ila_params *p = ila_params_lwtunnel(lwtstate); 111 112 if (nla_put_u64(skb, ILA_ATTR_LOCATOR, (__force u64)p->locator)) 113 goto nla_put_failure; 114 115 return 0; 116 117 nla_put_failure: 118 return -EMSGSIZE; 119 } 120 121 static int ila_encap_nlsize(struct lwtunnel_state *lwtstate) 122 { 123 return nla_total_size(sizeof(u64)); /* ILA_ATTR_LOCATOR */ 124 } 125 126 static int ila_encap_cmp(struct lwtunnel_state *a, struct lwtunnel_state *b) 127 { 128 struct ila_params *a_p = ila_params_lwtunnel(a); 129 struct ila_params *b_p = ila_params_lwtunnel(b); 130 131 return (a_p->locator != b_p->locator); 132 } 133 134 static const struct lwtunnel_encap_ops ila_encap_ops = { 135 .build_state = ila_build_state, 136 .output = ila_output, 137 .input = ila_input, 138 .fill_encap = ila_fill_encap_info, 139 .get_encap_size = ila_encap_nlsize, 140 .cmp_encap = ila_encap_cmp, 141 }; 142 143 int ila_lwt_init(void) 144 { 145 return lwtunnel_encap_add_ops(&ila_encap_ops, LWTUNNEL_ENCAP_ILA); 146 } 147 148 void ila_lwt_fini(void) 149 { 150 lwtunnel_encap_del_ops(&ila_encap_ops, LWTUNNEL_ENCAP_ILA); 151 } 152