diff options
Diffstat (limited to 'net/openvswitch')
-rw-r--r-- | net/openvswitch/actions.c | 93 | ||||
-rw-r--r-- | net/openvswitch/datapath.c | 20 |
2 files changed, 113 insertions, 0 deletions
diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c index 08114478cb85..a58ed276f70d 100644 --- a/net/openvswitch/actions.c +++ b/net/openvswitch/actions.c | |||
@@ -28,6 +28,7 @@ | |||
28 | #include <linux/if_arp.h> | 28 | #include <linux/if_arp.h> |
29 | #include <linux/if_vlan.h> | 29 | #include <linux/if_vlan.h> |
30 | #include <net/ip.h> | 30 | #include <net/ip.h> |
31 | #include <net/ipv6.h> | ||
31 | #include <net/checksum.h> | 32 | #include <net/checksum.h> |
32 | #include <net/dsfield.h> | 33 | #include <net/dsfield.h> |
33 | 34 | ||
@@ -162,6 +163,53 @@ static void set_ip_addr(struct sk_buff *skb, struct iphdr *nh, | |||
162 | *addr = new_addr; | 163 | *addr = new_addr; |
163 | } | 164 | } |
164 | 165 | ||
166 | static void update_ipv6_checksum(struct sk_buff *skb, u8 l4_proto, | ||
167 | __be32 addr[4], const __be32 new_addr[4]) | ||
168 | { | ||
169 | int transport_len = skb->len - skb_transport_offset(skb); | ||
170 | |||
171 | if (l4_proto == IPPROTO_TCP) { | ||
172 | if (likely(transport_len >= sizeof(struct tcphdr))) | ||
173 | inet_proto_csum_replace16(&tcp_hdr(skb)->check, skb, | ||
174 | addr, new_addr, 1); | ||
175 | } else if (l4_proto == IPPROTO_UDP) { | ||
176 | if (likely(transport_len >= sizeof(struct udphdr))) { | ||
177 | struct udphdr *uh = udp_hdr(skb); | ||
178 | |||
179 | if (uh->check || skb->ip_summed == CHECKSUM_PARTIAL) { | ||
180 | inet_proto_csum_replace16(&uh->check, skb, | ||
181 | addr, new_addr, 1); | ||
182 | if (!uh->check) | ||
183 | uh->check = CSUM_MANGLED_0; | ||
184 | } | ||
185 | } | ||
186 | } | ||
187 | } | ||
188 | |||
189 | static void set_ipv6_addr(struct sk_buff *skb, u8 l4_proto, | ||
190 | __be32 addr[4], const __be32 new_addr[4], | ||
191 | bool recalculate_csum) | ||
192 | { | ||
193 | if (recalculate_csum) | ||
194 | update_ipv6_checksum(skb, l4_proto, addr, new_addr); | ||
195 | |||
196 | skb->rxhash = 0; | ||
197 | memcpy(addr, new_addr, sizeof(__be32[4])); | ||
198 | } | ||
199 | |||
200 | static void set_ipv6_tc(struct ipv6hdr *nh, u8 tc) | ||
201 | { | ||
202 | nh->priority = tc >> 4; | ||
203 | nh->flow_lbl[0] = (nh->flow_lbl[0] & 0x0F) | ((tc & 0x0F) << 4); | ||
204 | } | ||
205 | |||
206 | static void set_ipv6_fl(struct ipv6hdr *nh, u32 fl) | ||
207 | { | ||
208 | nh->flow_lbl[0] = (nh->flow_lbl[0] & 0xF0) | (fl & 0x000F0000) >> 16; | ||
209 | nh->flow_lbl[1] = (fl & 0x0000FF00) >> 8; | ||
210 | nh->flow_lbl[2] = fl & 0x000000FF; | ||
211 | } | ||
212 | |||
165 | static void set_ip_ttl(struct sk_buff *skb, struct iphdr *nh, u8 new_ttl) | 213 | static void set_ip_ttl(struct sk_buff *skb, struct iphdr *nh, u8 new_ttl) |
166 | { | 214 | { |
167 | csum_replace2(&nh->check, htons(nh->ttl << 8), htons(new_ttl << 8)); | 215 | csum_replace2(&nh->check, htons(nh->ttl << 8), htons(new_ttl << 8)); |
@@ -195,6 +243,47 @@ static int set_ipv4(struct sk_buff *skb, const struct ovs_key_ipv4 *ipv4_key) | |||
195 | return 0; | 243 | return 0; |
196 | } | 244 | } |
197 | 245 | ||
246 | static int set_ipv6(struct sk_buff *skb, const struct ovs_key_ipv6 *ipv6_key) | ||
247 | { | ||
248 | struct ipv6hdr *nh; | ||
249 | int err; | ||
250 | __be32 *saddr; | ||
251 | __be32 *daddr; | ||
252 | |||
253 | err = make_writable(skb, skb_network_offset(skb) + | ||
254 | sizeof(struct ipv6hdr)); | ||
255 | if (unlikely(err)) | ||
256 | return err; | ||
257 | |||
258 | nh = ipv6_hdr(skb); | ||
259 | saddr = (__be32 *)&nh->saddr; | ||
260 | daddr = (__be32 *)&nh->daddr; | ||
261 | |||
262 | if (memcmp(ipv6_key->ipv6_src, saddr, sizeof(ipv6_key->ipv6_src))) | ||
263 | set_ipv6_addr(skb, ipv6_key->ipv6_proto, saddr, | ||
264 | ipv6_key->ipv6_src, true); | ||
265 | |||
266 | if (memcmp(ipv6_key->ipv6_dst, daddr, sizeof(ipv6_key->ipv6_dst))) { | ||
267 | unsigned int offset = 0; | ||
268 | int flags = IP6_FH_F_SKIP_RH; | ||
269 | bool recalc_csum = true; | ||
270 | |||
271 | if (ipv6_ext_hdr(nh->nexthdr)) | ||
272 | recalc_csum = ipv6_find_hdr(skb, &offset, | ||
273 | NEXTHDR_ROUTING, NULL, | ||
274 | &flags) != NEXTHDR_ROUTING; | ||
275 | |||
276 | set_ipv6_addr(skb, ipv6_key->ipv6_proto, daddr, | ||
277 | ipv6_key->ipv6_dst, recalc_csum); | ||
278 | } | ||
279 | |||
280 | set_ipv6_tc(nh, ipv6_key->ipv6_tclass); | ||
281 | set_ipv6_fl(nh, ntohl(ipv6_key->ipv6_label)); | ||
282 | nh->hop_limit = ipv6_key->ipv6_hlimit; | ||
283 | |||
284 | return 0; | ||
285 | } | ||
286 | |||
198 | /* Must follow make_writable() since that can move the skb data. */ | 287 | /* Must follow make_writable() since that can move the skb data. */ |
199 | static void set_tp_port(struct sk_buff *skb, __be16 *port, | 288 | static void set_tp_port(struct sk_buff *skb, __be16 *port, |
200 | __be16 new_port, __sum16 *check) | 289 | __be16 new_port, __sum16 *check) |
@@ -347,6 +436,10 @@ static int execute_set_action(struct sk_buff *skb, | |||
347 | err = set_ipv4(skb, nla_data(nested_attr)); | 436 | err = set_ipv4(skb, nla_data(nested_attr)); |
348 | break; | 437 | break; |
349 | 438 | ||
439 | case OVS_KEY_ATTR_IPV6: | ||
440 | err = set_ipv6(skb, nla_data(nested_attr)); | ||
441 | break; | ||
442 | |||
350 | case OVS_KEY_ATTR_TCP: | 443 | case OVS_KEY_ATTR_TCP: |
351 | err = set_tcp(skb, nla_data(nested_attr)); | 444 | err = set_tcp(skb, nla_data(nested_attr)); |
352 | break; | 445 | break; |
diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c index 4c4b62ccc7d7..fd4a6a4ce3cd 100644 --- a/net/openvswitch/datapath.c +++ b/net/openvswitch/datapath.c | |||
@@ -479,6 +479,7 @@ static int validate_set(const struct nlattr *a, | |||
479 | 479 | ||
480 | switch (key_type) { | 480 | switch (key_type) { |
481 | const struct ovs_key_ipv4 *ipv4_key; | 481 | const struct ovs_key_ipv4 *ipv4_key; |
482 | const struct ovs_key_ipv6 *ipv6_key; | ||
482 | 483 | ||
483 | case OVS_KEY_ATTR_PRIORITY: | 484 | case OVS_KEY_ATTR_PRIORITY: |
484 | case OVS_KEY_ATTR_ETHERNET: | 485 | case OVS_KEY_ATTR_ETHERNET: |
@@ -500,6 +501,25 @@ static int validate_set(const struct nlattr *a, | |||
500 | 501 | ||
501 | break; | 502 | break; |
502 | 503 | ||
504 | case OVS_KEY_ATTR_IPV6: | ||
505 | if (flow_key->eth.type != htons(ETH_P_IPV6)) | ||
506 | return -EINVAL; | ||
507 | |||
508 | if (!flow_key->ip.proto) | ||
509 | return -EINVAL; | ||
510 | |||
511 | ipv6_key = nla_data(ovs_key); | ||
512 | if (ipv6_key->ipv6_proto != flow_key->ip.proto) | ||
513 | return -EINVAL; | ||
514 | |||
515 | if (ipv6_key->ipv6_frag != flow_key->ip.frag) | ||
516 | return -EINVAL; | ||
517 | |||
518 | if (ntohl(ipv6_key->ipv6_label) & 0xFFF00000) | ||
519 | return -EINVAL; | ||
520 | |||
521 | break; | ||
522 | |||
503 | case OVS_KEY_ATTR_TCP: | 523 | case OVS_KEY_ATTR_TCP: |
504 | if (flow_key->ip.proto != IPPROTO_TCP) | 524 | if (flow_key->ip.proto != IPPROTO_TCP) |
505 | return -EINVAL; | 525 | return -EINVAL; |