Blame view

net/mpls/mpls_iptunnel.c 7.65 KB
e3e4712ec   Roopa Prabhu   mpls: ip tunnel s...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
  /*
   * mpls tunnels	An implementation mpls tunnels using the light weight tunnel
   *		infrastructure
   *
   * Authors:	Roopa Prabhu, <roopa@cumulusnetworks.com>
   *
   *		This program is free software; you can redistribute it and/or
   *		modify it under the terms of the GNU General Public License
   *		as published by the Free Software Foundation; either version
   *		2 of the License, or (at your option) any later version.
   *
   */
  #include <linux/types.h>
  #include <linux/skbuff.h>
  #include <linux/net.h>
  #include <linux/module.h>
  #include <linux/mpls.h>
  #include <linux/vmalloc.h>
  #include <net/ip.h>
  #include <net/dst.h>
  #include <net/lwtunnel.h>
  #include <net/netevent.h>
  #include <net/netns/generic.h>
  #include <net/ip6_fib.h>
  #include <net/route.h>
  #include <net/mpls_iptunnel.h>
  #include <linux/mpls_iptunnel.h>
  #include "internal.h"
  
  static const struct nla_policy mpls_iptunnel_policy[MPLS_IPTUNNEL_MAX + 1] = {
  	[MPLS_IPTUNNEL_DST]	= { .type = NLA_U32 },
a59166e47   Robert Shearman   mpls: allow TTL p...
32
  	[MPLS_IPTUNNEL_TTL]	= { .type = NLA_U8 },
e3e4712ec   Roopa Prabhu   mpls: ip tunnel s...
33
34
35
36
37
38
39
  };
  
  static unsigned int mpls_encap_size(struct mpls_iptunnel_encap *en)
  {
  	/* The size of the layer 2.5 labels to be added for this route */
  	return en->labels * sizeof(struct mpls_shim_hdr);
  }
14972cbd3   Roopa Prabhu   net: lwtunnel: Ha...
40
  static int mpls_xmit(struct sk_buff *skb)
e3e4712ec   Roopa Prabhu   mpls: ip tunnel s...
41
42
43
44
45
46
47
48
49
50
  {
  	struct mpls_iptunnel_encap *tun_encap_info;
  	struct mpls_shim_hdr *hdr;
  	struct net_device *out_dev;
  	unsigned int hh_len;
  	unsigned int new_header_size;
  	unsigned int mtu;
  	struct dst_entry *dst = skb_dst(skb);
  	struct rtable *rt = NULL;
  	struct rt6_info *rt6 = NULL;
27d691056   Robert Shearman   mpls: Packet stats
51
  	struct mpls_dev *out_mdev;
a59166e47   Robert Shearman   mpls: allow TTL p...
52
  	struct net *net;
e3e4712ec   Roopa Prabhu   mpls: ip tunnel s...
53
54
55
56
  	int err = 0;
  	bool bos;
  	int i;
  	unsigned int ttl;
27d691056   Robert Shearman   mpls: Packet stats
57
58
  	/* Find the output device */
  	out_dev = dst->dev;
a59166e47   Robert Shearman   mpls: allow TTL p...
59
  	net = dev_net(out_dev);
e3e4712ec   Roopa Prabhu   mpls: ip tunnel s...
60
61
  
  	skb_orphan(skb);
e3e4712ec   Roopa Prabhu   mpls: ip tunnel s...
62
  	if (!mpls_output_possible(out_dev) ||
61adedf3e   Jiri Benc   route: move lwtun...
63
  	    !dst->lwtstate || skb_warn_if_lro(skb))
e3e4712ec   Roopa Prabhu   mpls: ip tunnel s...
64
65
66
  		goto drop;
  
  	skb_forward_csum(skb);
61adedf3e   Jiri Benc   route: move lwtun...
67
  	tun_encap_info = mpls_lwtunnel_encap(dst->lwtstate);
e3e4712ec   Roopa Prabhu   mpls: ip tunnel s...
68

a59166e47   Robert Shearman   mpls: allow TTL p...
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
  	/* Obtain the ttl using the following set of rules.
  	 *
  	 * LWT ttl propagation setting:
  	 *  - disabled => use default TTL value from LWT
  	 *  - enabled  => use TTL value from IPv4/IPv6 header
  	 *  - default  =>
  	 *   Global ttl propagation setting:
  	 *    - disabled => use default TTL value from global setting
  	 *    - enabled => use TTL value from IPv4/IPv6 header
  	 */
  	if (dst->ops->family == AF_INET) {
  		if (tun_encap_info->ttl_propagate == MPLS_TTL_PROP_DISABLED)
  			ttl = tun_encap_info->default_ttl;
  		else if (tun_encap_info->ttl_propagate == MPLS_TTL_PROP_DEFAULT &&
  			 !net->mpls.ip_ttl_propagate)
  			ttl = net->mpls.default_ttl;
  		else
  			ttl = ip_hdr(skb)->ttl;
  		rt = (struct rtable *)dst;
  	} else if (dst->ops->family == AF_INET6) {
  		if (tun_encap_info->ttl_propagate == MPLS_TTL_PROP_DISABLED)
  			ttl = tun_encap_info->default_ttl;
  		else if (tun_encap_info->ttl_propagate == MPLS_TTL_PROP_DEFAULT &&
  			 !net->mpls.ip_ttl_propagate)
  			ttl = net->mpls.default_ttl;
  		else
  			ttl = ipv6_hdr(skb)->hop_limit;
  		rt6 = (struct rt6_info *)dst;
  	} else {
  		goto drop;
  	}
e3e4712ec   Roopa Prabhu   mpls: ip tunnel s...
100
101
102
103
104
105
106
107
108
109
110
111
112
  	/* Verify the destination can hold the packet */
  	new_header_size = mpls_encap_size(tun_encap_info);
  	mtu = mpls_dev_mtu(out_dev);
  	if (mpls_pkt_too_big(skb, mtu - new_header_size))
  		goto drop;
  
  	hh_len = LL_RESERVED_SPACE(out_dev);
  	if (!out_dev->header_ops)
  		hh_len = 0;
  
  	/* Ensure there is enough space for the headers in the skb */
  	if (skb_cow(skb, hh_len + new_header_size))
  		goto drop;
48d2ab609   David Ahern   net: mpls: Fixups...
113
114
  	skb_set_inner_protocol(skb, skb->protocol);
  	skb_reset_inner_network_header(skb);
e3e4712ec   Roopa Prabhu   mpls: ip tunnel s...
115
  	skb_push(skb, new_header_size);
48d2ab609   David Ahern   net: mpls: Fixups...
116

e3e4712ec   Roopa Prabhu   mpls: ip tunnel s...
117
118
119
120
121
122
123
124
125
126
127
128
129
  	skb_reset_network_header(skb);
  
  	skb->dev = out_dev;
  	skb->protocol = htons(ETH_P_MPLS_UC);
  
  	/* Push the new labels */
  	hdr = mpls_hdr(skb);
  	bos = true;
  	for (i = tun_encap_info->labels - 1; i >= 0; i--) {
  		hdr[i] = mpls_entry_encode(tun_encap_info->label[i],
  					   ttl, 0, bos);
  		bos = false;
  	}
27d691056   Robert Shearman   mpls: Packet stats
130
  	mpls_stats_inc_outucastpkts(out_dev, skb);
e3e4712ec   Roopa Prabhu   mpls: ip tunnel s...
131
132
133
134
135
136
137
138
139
140
  	if (rt)
  		err = neigh_xmit(NEIGH_ARP_TABLE, out_dev, &rt->rt_gateway,
  				 skb);
  	else if (rt6)
  		err = neigh_xmit(NEIGH_ND_TABLE, out_dev, &rt6->rt6i_gateway,
  				 skb);
  	if (err)
  		net_dbg_ratelimited("%s: packet transmission failed: %d
  ",
  				    __func__, err);
14972cbd3   Roopa Prabhu   net: lwtunnel: Ha...
141
  	return LWTUNNEL_XMIT_DONE;
e3e4712ec   Roopa Prabhu   mpls: ip tunnel s...
142
143
  
  drop:
27d691056   Robert Shearman   mpls: Packet stats
144
145
146
  	out_mdev = out_dev ? mpls_dev_get(out_dev) : NULL;
  	if (out_mdev)
  		MPLS_INC_STATS(out_mdev, tx_errors);
e3e4712ec   Roopa Prabhu   mpls: ip tunnel s...
147
148
149
  	kfree_skb(skb);
  	return -EINVAL;
  }
30357d7d8   David Ahern   lwtunnel: remove ...
150
  static int mpls_build_state(struct nlattr *nla,
127eb7cd3   Tom Herbert   lwt: Add cfg argu...
151
  			    unsigned int family, const void *cfg,
9ae287274   David Ahern   net: add extack a...
152
153
  			    struct lwtunnel_state **ts,
  			    struct netlink_ext_ack *extack)
e3e4712ec   Roopa Prabhu   mpls: ip tunnel s...
154
155
156
157
  {
  	struct mpls_iptunnel_encap *tun_encap_info;
  	struct nlattr *tb[MPLS_IPTUNNEL_MAX + 1];
  	struct lwtunnel_state *newts;
1511009cd   David Ahern   net: mpls: Increa...
158
  	u8 n_labels;
e3e4712ec   Roopa Prabhu   mpls: ip tunnel s...
159
160
161
  	int ret;
  
  	ret = nla_parse_nested(tb, MPLS_IPTUNNEL_MAX, nla,
9ae287274   David Ahern   net: add extack a...
162
  			       mpls_iptunnel_policy, extack);
e3e4712ec   Roopa Prabhu   mpls: ip tunnel s...
163
164
  	if (ret < 0)
  		return ret;
a1f10abe1   David Ahern   net: Fill in exta...
165
166
  	if (!tb[MPLS_IPTUNNEL_DST]) {
  		NL_SET_ERR_MSG(extack, "MPLS_IPTUNNEL_DST attribute is missing");
e3e4712ec   Roopa Prabhu   mpls: ip tunnel s...
167
  		return -EINVAL;
a1f10abe1   David Ahern   net: Fill in exta...
168
  	}
e3e4712ec   Roopa Prabhu   mpls: ip tunnel s...
169

1511009cd   David Ahern   net: mpls: Increa...
170
  	/* determine number of labels */
a1f10abe1   David Ahern   net: Fill in exta...
171
172
  	if (nla_get_labels(tb[MPLS_IPTUNNEL_DST], MAX_NEW_LABELS,
  			   &n_labels, NULL, extack))
1511009cd   David Ahern   net: mpls: Increa...
173
174
175
176
  		return -EINVAL;
  
  	newts = lwtunnel_state_alloc(sizeof(*tun_encap_info) +
  				     n_labels * sizeof(u32));
e3e4712ec   Roopa Prabhu   mpls: ip tunnel s...
177
178
  	if (!newts)
  		return -ENOMEM;
e3e4712ec   Roopa Prabhu   mpls: ip tunnel s...
179
  	tun_encap_info = mpls_lwtunnel_encap(newts);
1511009cd   David Ahern   net: mpls: Increa...
180
  	ret = nla_get_labels(tb[MPLS_IPTUNNEL_DST], n_labels,
a1f10abe1   David Ahern   net: Fill in exta...
181
182
  			     &tun_encap_info->labels, tun_encap_info->label,
  			     extack);
e3e4712ec   Roopa Prabhu   mpls: ip tunnel s...
183
184
  	if (ret)
  		goto errout;
a59166e47   Robert Shearman   mpls: allow TTL p...
185
186
187
188
189
190
191
192
193
194
  
  	tun_encap_info->ttl_propagate = MPLS_TTL_PROP_DEFAULT;
  
  	if (tb[MPLS_IPTUNNEL_TTL]) {
  		tun_encap_info->default_ttl = nla_get_u8(tb[MPLS_IPTUNNEL_TTL]);
  		/* TTL 0 implies propagate from IP header */
  		tun_encap_info->ttl_propagate = tun_encap_info->default_ttl ?
  			MPLS_TTL_PROP_DISABLED :
  			MPLS_TTL_PROP_ENABLED;
  	}
e3e4712ec   Roopa Prabhu   mpls: ip tunnel s...
195
  	newts->type = LWTUNNEL_ENCAP_MPLS;
14972cbd3   Roopa Prabhu   net: lwtunnel: Ha...
196
197
  	newts->flags |= LWTUNNEL_STATE_XMIT_REDIRECT;
  	newts->headroom = mpls_encap_size(tun_encap_info);
e3e4712ec   Roopa Prabhu   mpls: ip tunnel s...
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
  
  	*ts = newts;
  
  	return 0;
  
  errout:
  	kfree(newts);
  	*ts = NULL;
  
  	return ret;
  }
  
  static int mpls_fill_encap_info(struct sk_buff *skb,
  				struct lwtunnel_state *lwtstate)
  {
  	struct mpls_iptunnel_encap *tun_encap_info;
  	
  	tun_encap_info = mpls_lwtunnel_encap(lwtstate);
  
  	if (nla_put_labels(skb, MPLS_IPTUNNEL_DST, tun_encap_info->labels,
  			   tun_encap_info->label))
  		goto nla_put_failure;
a59166e47   Robert Shearman   mpls: allow TTL p...
220
221
222
  	if (tun_encap_info->ttl_propagate != MPLS_TTL_PROP_DEFAULT &&
  	    nla_put_u8(skb, MPLS_IPTUNNEL_TTL, tun_encap_info->default_ttl))
  		goto nla_put_failure;
e3e4712ec   Roopa Prabhu   mpls: ip tunnel s...
223
224
225
226
227
228
229
230
231
  	return 0;
  
  nla_put_failure:
  	return -EMSGSIZE;
  }
  
  static int mpls_encap_nlsize(struct lwtunnel_state *lwtstate)
  {
  	struct mpls_iptunnel_encap *tun_encap_info;
a59166e47   Robert Shearman   mpls: allow TTL p...
232
  	int nlsize;
e3e4712ec   Roopa Prabhu   mpls: ip tunnel s...
233
234
  
  	tun_encap_info = mpls_lwtunnel_encap(lwtstate);
a59166e47   Robert Shearman   mpls: allow TTL p...
235
236
237
238
239
240
  	nlsize = nla_total_size(tun_encap_info->labels * 4);
  
  	if (tun_encap_info->ttl_propagate != MPLS_TTL_PROP_DEFAULT)
  		nlsize += nla_total_size(1);
  
  	return nlsize;
e3e4712ec   Roopa Prabhu   mpls: ip tunnel s...
241
242
243
244
245
246
247
  }
  
  static int mpls_encap_cmp(struct lwtunnel_state *a, struct lwtunnel_state *b)
  {
  	struct mpls_iptunnel_encap *a_hdr = mpls_lwtunnel_encap(a);
  	struct mpls_iptunnel_encap *b_hdr = mpls_lwtunnel_encap(b);
  	int l;
a59166e47   Robert Shearman   mpls: allow TTL p...
248
249
250
  	if (a_hdr->labels != b_hdr->labels ||
  	    a_hdr->ttl_propagate != b_hdr->ttl_propagate ||
  	    a_hdr->default_ttl != b_hdr->default_ttl)
e3e4712ec   Roopa Prabhu   mpls: ip tunnel s...
251
  		return 1;
1511009cd   David Ahern   net: mpls: Increa...
252
  	for (l = 0; l < a_hdr->labels; l++)
e3e4712ec   Roopa Prabhu   mpls: ip tunnel s...
253
254
255
256
257
258
259
  		if (a_hdr->label[l] != b_hdr->label[l])
  			return 1;
  	return 0;
  }
  
  static const struct lwtunnel_encap_ops mpls_iptun_ops = {
  	.build_state = mpls_build_state,
14972cbd3   Roopa Prabhu   net: lwtunnel: Ha...
260
  	.xmit = mpls_xmit,
e3e4712ec   Roopa Prabhu   mpls: ip tunnel s...
261
262
263
  	.fill_encap = mpls_fill_encap_info,
  	.get_encap_size = mpls_encap_nlsize,
  	.cmp_encap = mpls_encap_cmp,
88ff7334f   Robert Shearman   net: Specify the ...
264
  	.owner = THIS_MODULE,
e3e4712ec   Roopa Prabhu   mpls: ip tunnel s...
265
266
267
268
269
270
271
272
273
274
275
276
277
  };
  
  static int __init mpls_iptunnel_init(void)
  {
  	return lwtunnel_encap_add_ops(&mpls_iptun_ops, LWTUNNEL_ENCAP_MPLS);
  }
  module_init(mpls_iptunnel_init);
  
  static void __exit mpls_iptunnel_exit(void)
  {
  	lwtunnel_encap_del_ops(&mpls_iptun_ops, LWTUNNEL_ENCAP_MPLS);
  }
  module_exit(mpls_iptunnel_exit);
b2b04edce   Robert Shearman   mpls: autoload lw...
278
  MODULE_ALIAS_RTNL_LWT(MPLS);
e3e4712ec   Roopa Prabhu   mpls: ip tunnel s...
279
280
  MODULE_DESCRIPTION("MultiProtocol Label Switching IP Tunnels");
  MODULE_LICENSE("GPL v2");