]>
Commit | Line | Data |
---|---|---|
a6ae068b LJ |
1 | /* |
2 | * Copyright (c) 2011 Nicira, Inc. | |
3 | * Copyright (c) 2013 Cisco Systems, Inc. | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or | |
6 | * modify it under the terms of version 2 of the GNU General Public | |
7 | * License as published by the Free Software Foundation. | |
8 | * | |
9 | * This program is distributed in the hope that it will be useful, but | |
10 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
12 | * General Public License for more details. | |
13 | * | |
14 | * You should have received a copy of the GNU General Public License | |
15 | * along with this program; if not, write to the Free Software | |
16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | |
17 | * 02110-1301, USA | |
18 | */ | |
19 | ||
20 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | |
21 | ||
22 | #include <linux/version.h> | |
a6ae068b LJ |
23 | |
24 | #include <linux/in.h> | |
25 | #include <linux/ip.h> | |
a6ae068b | 26 | #include <linux/net.h> |
85c9de19 | 27 | #include <linux/rculist.h> |
a6ae068b LJ |
28 | #include <linux/udp.h> |
29 | ||
30 | #include <net/icmp.h> | |
31 | #include <net/ip.h> | |
11aa8dff | 32 | #include <net/route.h> |
a6ae068b | 33 | #include <net/udp.h> |
3dd9bffd | 34 | #include <net/udp_tunnel.h> |
11aa8dff | 35 | #include <net/xfrm.h> |
a6ae068b LJ |
36 | |
37 | #include "datapath.h" | |
3cf54a56 | 38 | #include "gso.h" |
a6ae068b LJ |
39 | #include "vport.h" |
40 | ||
a6ae068b LJ |
41 | /* |
42 | * LISP encapsulation header: | |
43 | * | |
44 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | |
45 | * |N|L|E|V|I|flags| Nonce/Map-Version | | |
46 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | |
47 | * | Instance ID/Locator Status Bits | | |
48 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | |
49 | * | |
50 | */ | |
51 | ||
52 | /** | |
53 | * struct lisphdr - LISP header | |
54 | * @nonce_present: Flag indicating the presence of a 24 bit nonce value. | |
55 | * @locator_status_bits_present: Flag indicating the presence of Locator Status | |
56 | * Bits (LSB). | |
57 | * @solicit_echo_nonce: Flag indicating the use of the echo noncing mechanism. | |
58 | * @map_version_present: Flag indicating the use of mapping versioning. | |
59 | * @instance_id_present: Flag indicating the presence of a 24 bit Instance ID. | |
60 | * @reserved_flags: 3 bits reserved for future flags. | |
61 | * @nonce: 24 bit nonce value. | |
62 | * @map_version: 24 bit mapping version. | |
63 | * @locator_status_bits: Locator Status Bits: 32 bits when instance_id_present | |
64 | * is not set, 8 bits when it is. | |
65 | * @instance_id: 24 bit Instance ID | |
66 | */ | |
67 | struct lisphdr { | |
68 | #ifdef __LITTLE_ENDIAN_BITFIELD | |
69 | __u8 reserved_flags:3; | |
70 | __u8 instance_id_present:1; | |
71 | __u8 map_version_present:1; | |
72 | __u8 solicit_echo_nonce:1; | |
73 | __u8 locator_status_bits_present:1; | |
74 | __u8 nonce_present:1; | |
75 | #else | |
76 | __u8 nonce_present:1; | |
77 | __u8 locator_status_bits_present:1; | |
78 | __u8 solicit_echo_nonce:1; | |
79 | __u8 map_version_present:1; | |
80 | __u8 instance_id_present:1; | |
81 | __u8 reserved_flags:3; | |
82 | #endif | |
83 | union { | |
84 | __u8 nonce[3]; | |
85 | __u8 map_version[3]; | |
86 | } u1; | |
87 | union { | |
88 | __be32 locator_status_bits; | |
89 | struct { | |
90 | __u8 instance_id[3]; | |
91 | __u8 locator_status_bits; | |
92 | } word2; | |
93 | } u2; | |
94 | }; | |
95 | ||
96 | #define LISP_HLEN (sizeof(struct udphdr) + sizeof(struct lisphdr)) | |
97 | ||
a6ae068b LJ |
98 | /** |
99 | * struct lisp_port - Keeps track of open UDP ports | |
c405d282 PS |
100 | * @dst_port: lisp UDP port no. |
101 | * @list: list element in @lisp_ports. | |
102 | * @lisp_rcv_socket: The socket created for this port number. | |
103 | * @name: vport name. | |
a6ae068b LJ |
104 | */ |
105 | struct lisp_port { | |
c405d282 | 106 | __be16 dst_port; |
a6ae068b | 107 | struct list_head list; |
a6ae068b | 108 | struct socket *lisp_rcv_socket; |
c405d282 | 109 | char name[IFNAMSIZ]; |
a6ae068b LJ |
110 | }; |
111 | ||
112 | static LIST_HEAD(lisp_ports); | |
113 | ||
c405d282 PS |
114 | static inline struct lisp_port *lisp_vport(const struct vport *vport) |
115 | { | |
116 | return vport_priv(vport); | |
117 | } | |
118 | ||
85c9de19 | 119 | static struct lisp_port *lisp_find_port(struct net *net, __be16 port) |
a6ae068b LJ |
120 | { |
121 | struct lisp_port *lisp_port; | |
122 | ||
85c9de19 | 123 | list_for_each_entry_rcu(lisp_port, &lisp_ports, list) { |
c405d282 | 124 | if (lisp_port->dst_port == port && |
a6ae068b LJ |
125 | net_eq(sock_net(lisp_port->lisp_rcv_socket->sk), net)) |
126 | return lisp_port; | |
127 | } | |
128 | ||
129 | return NULL; | |
130 | } | |
131 | ||
132 | static inline struct lisphdr *lisp_hdr(const struct sk_buff *skb) | |
133 | { | |
134 | return (struct lisphdr *)(udp_hdr(skb) + 1); | |
135 | } | |
136 | ||
a6ae068b LJ |
137 | /* Convert 64 bit tunnel ID to 24 bit Instance ID. */ |
138 | static void tunnel_id_to_instance_id(__be64 tun_id, __u8 *iid) | |
139 | { | |
140 | ||
141 | #ifdef __BIG_ENDIAN | |
142 | iid[0] = (__force __u8)(tun_id >> 16); | |
143 | iid[1] = (__force __u8)(tun_id >> 8); | |
144 | iid[2] = (__force __u8)tun_id; | |
145 | #else | |
146 | iid[0] = (__force __u8)((__force u64)tun_id >> 40); | |
147 | iid[1] = (__force __u8)((__force u64)tun_id >> 48); | |
148 | iid[2] = (__force __u8)((__force u64)tun_id >> 56); | |
149 | #endif | |
150 | } | |
151 | ||
152 | /* Convert 24 bit Instance ID to 64 bit tunnel ID. */ | |
153 | static __be64 instance_id_to_tunnel_id(__u8 *iid) | |
154 | { | |
155 | #ifdef __BIG_ENDIAN | |
156 | return (iid[0] << 16) | (iid[1] << 8) | iid[2]; | |
157 | #else | |
158 | return (__force __be64)(((__force u64)iid[0] << 40) | | |
159 | ((__force u64)iid[1] << 48) | | |
160 | ((__force u64)iid[2] << 56)); | |
161 | #endif | |
162 | } | |
163 | ||
11aa8dff PS |
164 | /* Compute source UDP port for outgoing packet. |
165 | * Currently we use the flow hash. | |
166 | */ | |
cb25142c | 167 | static u16 get_src_port(struct net *net, struct sk_buff *skb) |
11aa8dff | 168 | { |
e2f3178f | 169 | u32 hash = skb_get_hash(skb); |
11aa8dff | 170 | unsigned int range; |
5c89b171 PS |
171 | int high; |
172 | int low; | |
173 | ||
174 | if (!hash) { | |
e74d4817 PS |
175 | if (skb->protocol == htons(ETH_P_IP)) { |
176 | struct iphdr *iph; | |
177 | int size = (sizeof(iph->saddr) * 2) / sizeof(u32); | |
178 | ||
22c18a56 | 179 | iph = (struct iphdr *) skb_network_header(skb); |
e74d4817 PS |
180 | hash = jhash2((const u32 *)&iph->saddr, size, 0); |
181 | } else if (skb->protocol == htons(ETH_P_IPV6)) { | |
182 | struct ipv6hdr *ipv6hdr; | |
183 | ||
22c18a56 | 184 | ipv6hdr = (struct ipv6hdr *) skb_network_header(skb); |
e74d4817 PS |
185 | hash = jhash2((const u32 *)&ipv6hdr->saddr, |
186 | (sizeof(struct in6_addr) * 2) / sizeof(u32), 0); | |
187 | } else { | |
31125ebd JG |
188 | pr_warn_once("LISP inner protocol is not IP when " |
189 | "calculating hash.\n"); | |
e74d4817 | 190 | } |
5c89b171 | 191 | } |
11aa8dff | 192 | |
cb25142c | 193 | inet_get_local_port_range(net, &low, &high); |
11aa8dff PS |
194 | range = (high - low) + 1; |
195 | return (((u64) hash * range) >> 32) + low; | |
196 | } | |
197 | ||
22c18a56 | 198 | static void lisp_build_header(struct sk_buff *skb) |
a6ae068b | 199 | { |
22c18a56 | 200 | struct lisphdr *lisph; |
fb66fbd1 | 201 | const struct ovs_key_ipv4_tunnel *tun_key; |
a6ae068b | 202 | |
fb66fbd1 | 203 | tun_key = &OVS_CB(skb)->egress_tun_info->tunnel; |
a6ae068b | 204 | |
22c18a56 | 205 | lisph = (struct lisphdr *)__skb_push(skb, sizeof(struct lisphdr)); |
a6ae068b LJ |
206 | lisph->nonce_present = 0; /* We don't support echo nonce algorithm */ |
207 | lisph->locator_status_bits_present = 1; /* Set LSB */ | |
208 | lisph->solicit_echo_nonce = 0; /* No echo noncing */ | |
209 | lisph->map_version_present = 0; /* No mapping versioning, nonce instead */ | |
210 | lisph->instance_id_present = 1; /* Store the tun_id as Instance ID */ | |
211 | lisph->reserved_flags = 0; /* Reserved flags, set to 0 */ | |
212 | ||
213 | lisph->u1.nonce[0] = 0; | |
214 | lisph->u1.nonce[1] = 0; | |
215 | lisph->u1.nonce[2] = 0; | |
216 | ||
85c9de19 | 217 | tunnel_id_to_instance_id(tun_key->tun_id, &lisph->u2.word2.instance_id[0]); |
a6ae068b | 218 | lisph->u2.word2.locator_status_bits = 1; |
a6ae068b LJ |
219 | } |
220 | ||
221 | /* Called with rcu_read_lock and BH disabled. */ | |
222 | static int lisp_rcv(struct sock *sk, struct sk_buff *skb) | |
223 | { | |
85c9de19 | 224 | struct lisp_port *lisp_port; |
a6ae068b | 225 | struct lisphdr *lisph; |
a6ae068b | 226 | struct iphdr *iph, *inner_iph; |
f0cd669f | 227 | struct ovs_tunnel_info tun_info; |
a6ae068b | 228 | __be64 key; |
a6ae068b LJ |
229 | struct ethhdr *ethh; |
230 | __be16 protocol; | |
231 | ||
22c18a56 | 232 | lisp_port = rcu_dereference_sk_user_data(sk); |
85c9de19 PS |
233 | if (unlikely(!lisp_port)) |
234 | goto error; | |
235 | ||
971fd4ab | 236 | if (iptunnel_pull_header(skb, LISP_HLEN, 0)) |
a6ae068b LJ |
237 | goto error; |
238 | ||
239 | lisph = lisp_hdr(skb); | |
240 | ||
a6ae068b LJ |
241 | if (lisph->instance_id_present != 1) |
242 | key = 0; | |
243 | else | |
244 | key = instance_id_to_tunnel_id(&lisph->u2.word2.instance_id[0]); | |
245 | ||
a6ae068b | 246 | /* Save outer tunnel values */ |
85c9de19 | 247 | iph = ip_hdr(skb); |
8b7ea2d4 WZ |
248 | ovs_flow_tun_info_init(&tun_info, iph, |
249 | udp_hdr(skb)->source, udp_hdr(skb)->dest, | |
250 | key, TUNNEL_KEY, NULL, 0); | |
a6ae068b LJ |
251 | |
252 | /* Drop non-IP inner packets */ | |
253 | inner_iph = (struct iphdr *)(lisph + 1); | |
254 | switch (inner_iph->version) { | |
255 | case 4: | |
256 | protocol = htons(ETH_P_IP); | |
257 | break; | |
258 | case 6: | |
259 | protocol = htons(ETH_P_IPV6); | |
260 | break; | |
261 | default: | |
262 | goto error; | |
263 | } | |
971fd4ab | 264 | skb->protocol = protocol; |
a6ae068b LJ |
265 | |
266 | /* Add Ethernet header */ | |
267 | ethh = (struct ethhdr *)skb_push(skb, ETH_HLEN); | |
268 | memset(ethh, 0, ETH_HLEN); | |
269 | ethh->h_dest[0] = 0x02; | |
270 | ethh->h_source[0] = 0x02; | |
271 | ethh->h_proto = protocol; | |
272 | ||
3cfede14 PS |
273 | ovs_skb_postpush_rcsum(skb, skb->data, ETH_HLEN); |
274 | ||
f0cd669f | 275 | ovs_vport_receive(vport_from_priv(lisp_port), skb, &tun_info); |
a6ae068b LJ |
276 | goto out; |
277 | ||
278 | error: | |
279 | kfree_skb(skb); | |
280 | out: | |
281 | return 0; | |
282 | } | |
283 | ||
a6ae068b LJ |
284 | static int lisp_socket_init(struct lisp_port *lisp_port, struct net *net) |
285 | { | |
3dd9bffd | 286 | struct udp_port_cfg udp_conf; |
22c18a56 | 287 | struct udp_tunnel_sock_cfg tunnel_cfg; |
c405d282 | 288 | int err; |
a6ae068b | 289 | |
3dd9bffd | 290 | memset(&udp_conf, 0, sizeof(udp_conf)); |
a6ae068b | 291 | |
3dd9bffd JG |
292 | udp_conf.family = AF_INET; |
293 | udp_conf.local_ip.s_addr = htonl(INADDR_ANY); | |
294 | udp_conf.local_udp_port = lisp_port->dst_port; | |
a6ae068b | 295 | |
3dd9bffd JG |
296 | err = udp_sock_create(net, &udp_conf, &lisp_port->lisp_rcv_socket); |
297 | if (err < 0) { | |
298 | pr_warn("cannot register lisp protocol handler: %d\n", err); | |
299 | return err; | |
300 | } | |
a6ae068b | 301 | |
22c18a56 JG |
302 | tunnel_cfg.sk_user_data = lisp_port; |
303 | tunnel_cfg.encap_type = 1; | |
304 | tunnel_cfg.encap_rcv = lisp_rcv; | |
305 | tunnel_cfg.encap_destroy = NULL; | |
a6ae068b | 306 | |
22c18a56 | 307 | setup_udp_tunnel_sock(net, lisp_port->lisp_rcv_socket, &tunnel_cfg); |
a6ae068b LJ |
308 | |
309 | return 0; | |
a6ae068b LJ |
310 | } |
311 | ||
c405d282 | 312 | static int lisp_get_options(const struct vport *vport, struct sk_buff *skb) |
a6ae068b | 313 | { |
c405d282 | 314 | struct lisp_port *lisp_port = lisp_vport(vport); |
a6ae068b | 315 | |
c405d282 PS |
316 | if (nla_put_u16(skb, OVS_TUNNEL_ATTR_DST_PORT, ntohs(lisp_port->dst_port))) |
317 | return -EMSGSIZE; | |
318 | return 0; | |
a6ae068b LJ |
319 | } |
320 | ||
c405d282 | 321 | static void lisp_tnl_destroy(struct vport *vport) |
a6ae068b | 322 | { |
c405d282 PS |
323 | struct lisp_port *lisp_port = lisp_vport(vport); |
324 | ||
85c9de19 | 325 | list_del_rcu(&lisp_port->list); |
22c18a56 | 326 | udp_tunnel_sock_release(lisp_port->lisp_rcv_socket); |
c405d282 | 327 | ovs_vport_deferred_free(vport); |
85c9de19 PS |
328 | } |
329 | ||
c405d282 | 330 | static struct vport *lisp_tnl_create(const struct vport_parms *parms) |
85c9de19 | 331 | { |
c405d282 PS |
332 | struct net *net = ovs_dp_get_net(parms->dp); |
333 | struct nlattr *options = parms->options; | |
85c9de19 | 334 | struct lisp_port *lisp_port; |
c405d282 | 335 | struct vport *vport; |
a6ae068b LJ |
336 | struct nlattr *a; |
337 | int err; | |
338 | u16 dst_port; | |
a6ae068b LJ |
339 | |
340 | if (!options) { | |
341 | err = -EINVAL; | |
c405d282 | 342 | goto error; |
a6ae068b LJ |
343 | } |
344 | ||
345 | a = nla_find_nested(options, OVS_TUNNEL_ATTR_DST_PORT); | |
346 | if (a && nla_len(a) == sizeof(u16)) { | |
347 | dst_port = nla_get_u16(a); | |
348 | } else { | |
349 | /* Require destination port from userspace. */ | |
350 | err = -EINVAL; | |
c405d282 | 351 | goto error; |
a6ae068b LJ |
352 | } |
353 | ||
354 | /* Verify if we already have a socket created for this port */ | |
c405d282 | 355 | if (lisp_find_port(net, htons(dst_port))) { |
85c9de19 | 356 | err = -EEXIST; |
c405d282 | 357 | goto error; |
a6ae068b LJ |
358 | } |
359 | ||
c405d282 PS |
360 | vport = ovs_vport_alloc(sizeof(struct lisp_port), |
361 | &ovs_lisp_vport_ops, parms); | |
362 | if (IS_ERR(vport)) | |
363 | return vport; | |
a6ae068b | 364 | |
c405d282 PS |
365 | lisp_port = lisp_vport(vport); |
366 | lisp_port->dst_port = htons(dst_port); | |
367 | strncpy(lisp_port->name, parms->name, IFNAMSIZ); | |
a6ae068b LJ |
368 | |
369 | err = lisp_socket_init(lisp_port, net); | |
370 | if (err) | |
c405d282 | 371 | goto error_free; |
a6ae068b | 372 | |
c405d282 PS |
373 | list_add_tail_rcu(&lisp_port->list, &lisp_ports); |
374 | return vport; | |
a6ae068b | 375 | |
c405d282 PS |
376 | error_free: |
377 | ovs_vport_free(vport); | |
a6ae068b | 378 | error: |
c405d282 | 379 | return ERR_PTR(err); |
a6ae068b LJ |
380 | } |
381 | ||
3cf54a56 | 382 | static int lisp_send(struct vport *vport, struct sk_buff *skb) |
11aa8dff | 383 | { |
27914610 | 384 | struct ovs_key_ipv4_tunnel *tun_key; |
22c18a56 JG |
385 | struct lisp_port *lisp_port = lisp_vport(vport); |
386 | struct net *net = ovs_dp_get_net(vport->dp); | |
3cf54a56 | 387 | int network_offset = skb_network_offset(skb); |
11aa8dff | 388 | struct rtable *rt; |
3cf54a56 | 389 | int min_headroom; |
11aa8dff | 390 | __be32 saddr; |
22c18a56 | 391 | __be16 src_port, dst_port; |
3cf54a56 PS |
392 | __be16 df; |
393 | int sent_len; | |
11aa8dff | 394 | int err; |
3cf54a56 | 395 | |
93258bd7 PS |
396 | if (unlikely(!OVS_CB(skb)->egress_tun_info)) { |
397 | err = -EINVAL; | |
398 | goto error; | |
399 | } | |
3cf54a56 | 400 | |
fb66fbd1 | 401 | tun_key = &OVS_CB(skb)->egress_tun_info->tunnel; |
27914610 | 402 | |
3cf54a56 PS |
403 | if (skb->protocol != htons(ETH_P_IP) && |
404 | skb->protocol != htons(ETH_P_IPV6)) { | |
93258bd7 PS |
405 | err = 0; |
406 | goto error; | |
3cf54a56 | 407 | } |
11aa8dff PS |
408 | |
409 | /* Route lookup */ | |
f0cd669f | 410 | saddr = tun_key->ipv4_src; |
11aa8dff | 411 | rt = find_route(ovs_dp_get_net(vport->dp), |
f0cd669f JG |
412 | &saddr, tun_key->ipv4_dst, |
413 | IPPROTO_UDP, tun_key->ipv4_tos, | |
3025a772 | 414 | skb->mark); |
11aa8dff PS |
415 | if (IS_ERR(rt)) { |
416 | err = PTR_ERR(rt); | |
417 | goto error; | |
418 | } | |
419 | ||
11aa8dff | 420 | min_headroom = LL_RESERVED_SPACE(rt_dst(rt).dev) + rt_dst(rt).header_len |
3cf54a56 | 421 | + sizeof(struct iphdr) + LISP_HLEN; |
11aa8dff PS |
422 | |
423 | if (skb_headroom(skb) < min_headroom || skb_header_cloned(skb)) { | |
424 | int head_delta = SKB_DATA_ALIGN(min_headroom - | |
425 | skb_headroom(skb) + | |
426 | 16); | |
427 | ||
428 | err = pskb_expand_head(skb, max_t(int, head_delta, 0), | |
429 | 0, GFP_ATOMIC); | |
430 | if (unlikely(err)) | |
431 | goto err_free_rt; | |
432 | } | |
433 | ||
df7d2c59 | 434 | /* Reset l2 headers. */ |
3cf54a56 PS |
435 | skb_pull(skb, network_offset); |
436 | skb_reset_mac_header(skb); | |
df7d2c59 PS |
437 | vlan_set_tci(skb, 0); |
438 | ||
22c18a56 | 439 | skb = udp_tunnel_handle_offloads(skb, false, false); |
b1b3fb13 | 440 | if (IS_ERR(skb)) { |
93258bd7 PS |
441 | err = PTR_ERR(skb); |
442 | skb = NULL; | |
29c71cfa | 443 | goto err_free_rt; |
b1b3fb13 | 444 | } |
29c71cfa | 445 | |
22c18a56 JG |
446 | src_port = htons(get_src_port(net, skb)); |
447 | dst_port = lisp_port->dst_port; | |
448 | ||
449 | lisp_build_header(skb); | |
450 | ||
f6a0c895 | 451 | skb->ignore_df = 1; |
3cf54a56 | 452 | |
e4bafe59 JG |
453 | ovs_skb_set_inner_protocol(skb, skb->protocol); |
454 | ||
f0cd669f | 455 | df = tun_key->tun_flags & TUNNEL_DONT_FRAGMENT ? htons(IP_DF) : 0; |
7bdcee3e | 456 | sent_len = udp_tunnel_xmit_skb(rt, skb, saddr, tun_key->ipv4_dst, |
22c18a56 | 457 | tun_key->ipv4_tos, tun_key->ipv4_ttl, |
7bdcee3e | 458 | df, src_port, dst_port, false, true); |
11aa8dff | 459 | |
3cf54a56 | 460 | return sent_len > 0 ? sent_len + network_offset : sent_len; |
11aa8dff PS |
461 | |
462 | err_free_rt: | |
463 | ip_rt_put(rt); | |
464 | error: | |
93258bd7 | 465 | kfree_skb(skb); |
11aa8dff PS |
466 | return err; |
467 | } | |
468 | ||
c405d282 | 469 | static const char *lisp_get_name(const struct vport *vport) |
a6ae068b | 470 | { |
c405d282 PS |
471 | struct lisp_port *lisp_port = lisp_vport(vport); |
472 | return lisp_port->name; | |
a6ae068b LJ |
473 | } |
474 | ||
8b7ea2d4 WZ |
475 | static int lisp_get_egress_tun_info(struct vport *vport, struct sk_buff *skb, |
476 | struct ovs_tunnel_info *egress_tun_info) | |
477 | { | |
478 | struct net *net = ovs_dp_get_net(vport->dp); | |
479 | struct lisp_port *lisp_port = lisp_vport(vport); | |
480 | ||
481 | if (skb->protocol != htons(ETH_P_IP) && | |
482 | skb->protocol != htons(ETH_P_IPV6)) { | |
483 | return -EINVAL; | |
484 | } | |
485 | ||
486 | /* | |
487 | * Get tp_src and tp_dst, refert to lisp_build_header(). | |
488 | */ | |
489 | return ovs_tunnel_get_egress_info(egress_tun_info, net, | |
490 | OVS_CB(skb)->egress_tun_info, | |
491 | IPPROTO_UDP, skb->mark, | |
492 | htons(get_src_port(net, skb)), | |
493 | lisp_port->dst_port); | |
494 | } | |
495 | ||
a6ae068b | 496 | const struct vport_ops ovs_lisp_vport_ops = { |
8b7ea2d4 WZ |
497 | .type = OVS_VPORT_TYPE_LISP, |
498 | .create = lisp_tnl_create, | |
499 | .destroy = lisp_tnl_destroy, | |
500 | .get_name = lisp_get_name, | |
501 | .get_options = lisp_get_options, | |
502 | .send = lisp_send, | |
503 | .get_egress_tun_info = lisp_get_egress_tun_info, | |
a6ae068b | 504 | }; |