]>
Commit | Line | Data |
---|---|---|
96b82f6d YY |
1 | /* |
2 | * Network Service Header | |
3 | * | |
4 | * Copyright (c) 2017 Red Hat, Inc. -- Jiri Benc <jbenc@redhat.com> | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License version 2 as | |
8 | * published by the Free Software Foundation. | |
9 | */ | |
10 | ||
11 | #include <linux/netdevice.h> | |
12 | #include <linux/skbuff.h> | |
13 | #include <net/nsh.h> | |
14 | #include <net/tun_proto.h> | |
15 | ||
16 | int ovs_nsh_push(struct sk_buff *skb, const struct nshhdr *pushed_nh) | |
17 | { | |
18 | struct nshhdr *nh; | |
19 | size_t length = nsh_hdr_len(pushed_nh); | |
20 | u8 next_proto; | |
21 | ||
22 | if (skb->mac_len) { | |
23 | next_proto = TUN_P_ETHERNET; | |
24 | } else { | |
25 | next_proto = tun_p_from_eth_p(skb->protocol); | |
26 | if (!next_proto) | |
27 | return -EAFNOSUPPORT; | |
28 | } | |
29 | ||
30 | /* Add the NSH header */ | |
31 | if (skb_cow_head(skb, length) < 0) | |
32 | return -ENOMEM; | |
33 | ||
34 | skb_push(skb, length); | |
35 | nh = (struct nshhdr *)(skb->data); | |
36 | memcpy(nh, pushed_nh, length); | |
37 | nh->np = next_proto; | |
38 | skb_postpush_rcsum(skb, nh, length); | |
39 | ||
40 | skb->protocol = htons(ETH_P_NSH); | |
41 | skb_reset_mac_header(skb); | |
42 | skb_reset_network_header(skb); | |
43 | skb_reset_mac_len(skb); | |
44 | ||
45 | return 0; | |
46 | } | |
47 | EXPORT_SYMBOL_GPL(ovs_nsh_push); | |
48 | ||
49 | int ovs_nsh_pop(struct sk_buff *skb) | |
50 | { | |
51 | struct nshhdr *nh; | |
52 | size_t length; | |
53 | __be16 inner_proto; | |
54 | ||
55 | if (!pskb_may_pull(skb, NSH_BASE_HDR_LEN)) | |
56 | return -ENOMEM; | |
57 | nh = (struct nshhdr *)(skb->data); | |
58 | length = nsh_hdr_len(nh); | |
59 | inner_proto = tun_p_to_eth_p(nh->np); | |
60 | if (!pskb_may_pull(skb, length)) | |
61 | return -ENOMEM; | |
62 | ||
63 | if (!inner_proto) | |
64 | return -EAFNOSUPPORT; | |
65 | ||
66 | skb_pull_rcsum(skb, length); | |
67 | skb_reset_mac_header(skb); | |
68 | skb_reset_network_header(skb); | |
69 | skb_reset_mac_len(skb); | |
70 | skb->protocol = inner_proto; | |
71 | ||
72 | return 0; | |
73 | } | |
74 | EXPORT_SYMBOL_GPL(ovs_nsh_pop); | |
75 | ||
76 | static struct sk_buff *nsh_gso_segment(struct sk_buff *skb, | |
77 | netdev_features_t features) | |
78 | { | |
79 | struct sk_buff *segs = ERR_PTR(-EINVAL); | |
80 | unsigned int nsh_len, mac_len; | |
81 | __be16 proto; | |
82 | int nhoff; | |
83 | ||
84 | skb_reset_network_header(skb); | |
85 | ||
86 | nhoff = skb->network_header - skb->mac_header; | |
87 | mac_len = skb->mac_len; | |
88 | ||
89 | if (unlikely(!pskb_may_pull(skb, NSH_BASE_HDR_LEN))) | |
90 | goto out; | |
91 | nsh_len = nsh_hdr_len(nsh_hdr(skb)); | |
92 | if (unlikely(!pskb_may_pull(skb, nsh_len))) | |
93 | goto out; | |
94 | ||
95 | proto = tun_p_to_eth_p(nsh_hdr(skb)->np); | |
96 | if (!proto) | |
97 | goto out; | |
98 | ||
99 | __skb_pull(skb, nsh_len); | |
100 | ||
101 | skb_reset_mac_header(skb); | |
102 | skb_reset_mac_len(skb); | |
103 | skb->protocol = proto; | |
104 | ||
105 | features &= NETIF_F_SG; | |
106 | segs = skb_mac_gso_segment(skb, features); | |
107 | if (IS_ERR_OR_NULL(segs)) { | |
108 | skb_gso_error_unwind(skb, htons(ETH_P_NSH), nsh_len, | |
109 | skb->network_header - nhoff, | |
110 | mac_len); | |
111 | goto out; | |
112 | } | |
113 | ||
114 | for (skb = segs; skb; skb = skb->next) { | |
115 | skb->protocol = htons(ETH_P_NSH); | |
116 | __skb_push(skb, nsh_len); | |
117 | skb_set_mac_header(skb, -nhoff); | |
118 | skb->network_header = skb->mac_header + mac_len; | |
119 | skb->mac_len = mac_len; | |
120 | } | |
121 | ||
122 | out: | |
123 | return segs; | |
124 | } | |
125 | ||
126 | static struct packet_offload nsh_packet_offload __read_mostly = { | |
127 | .type = htons(ETH_P_NSH), | |
128 | .callbacks = { | |
129 | .gso_segment = nsh_gso_segment, | |
130 | }, | |
131 | }; | |
132 | ||
133 | int ovs_nsh_init(void) | |
134 | { | |
135 | dev_add_offload(&nsh_packet_offload); | |
136 | return 0; | |
137 | } | |
138 | ||
139 | void ovs_nsh_cleanup(void) | |
140 | { | |
141 | dev_remove_offload(&nsh_packet_offload); | |
142 | } |