diff options
Diffstat (limited to 'net/openvswitch/actions.c')
-rw-r--r-- | net/openvswitch/actions.c | 106 |
1 files changed, 105 insertions, 1 deletions
diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c index 922c133b1933..930b1b6e4cef 100644 --- a/net/openvswitch/actions.c +++ b/net/openvswitch/actions.c | |||
@@ -28,10 +28,12 @@ | |||
28 | #include <linux/in6.h> | 28 | #include <linux/in6.h> |
29 | #include <linux/if_arp.h> | 29 | #include <linux/if_arp.h> |
30 | #include <linux/if_vlan.h> | 30 | #include <linux/if_vlan.h> |
31 | |||
31 | #include <net/ip.h> | 32 | #include <net/ip.h> |
32 | #include <net/ipv6.h> | 33 | #include <net/ipv6.h> |
33 | #include <net/checksum.h> | 34 | #include <net/checksum.h> |
34 | #include <net/dsfield.h> | 35 | #include <net/dsfield.h> |
36 | #include <net/mpls.h> | ||
35 | #include <net/sctp/checksum.h> | 37 | #include <net/sctp/checksum.h> |
36 | 38 | ||
37 | #include "datapath.h" | 39 | #include "datapath.h" |
@@ -118,6 +120,92 @@ static int make_writable(struct sk_buff *skb, int write_len) | |||
118 | return pskb_expand_head(skb, 0, 0, GFP_ATOMIC); | 120 | return pskb_expand_head(skb, 0, 0, GFP_ATOMIC); |
119 | } | 121 | } |
120 | 122 | ||
123 | static int push_mpls(struct sk_buff *skb, | ||
124 | const struct ovs_action_push_mpls *mpls) | ||
125 | { | ||
126 | __be32 *new_mpls_lse; | ||
127 | struct ethhdr *hdr; | ||
128 | |||
129 | /* Networking stack do not allow simultaneous Tunnel and MPLS GSO. */ | ||
130 | if (skb->encapsulation) | ||
131 | return -ENOTSUPP; | ||
132 | |||
133 | if (skb_cow_head(skb, MPLS_HLEN) < 0) | ||
134 | return -ENOMEM; | ||
135 | |||
136 | skb_push(skb, MPLS_HLEN); | ||
137 | memmove(skb_mac_header(skb) - MPLS_HLEN, skb_mac_header(skb), | ||
138 | skb->mac_len); | ||
139 | skb_reset_mac_header(skb); | ||
140 | |||
141 | new_mpls_lse = (__be32 *)skb_mpls_header(skb); | ||
142 | *new_mpls_lse = mpls->mpls_lse; | ||
143 | |||
144 | if (skb->ip_summed == CHECKSUM_COMPLETE) | ||
145 | skb->csum = csum_add(skb->csum, csum_partial(new_mpls_lse, | ||
146 | MPLS_HLEN, 0)); | ||
147 | |||
148 | hdr = eth_hdr(skb); | ||
149 | hdr->h_proto = mpls->mpls_ethertype; | ||
150 | |||
151 | skb_set_inner_protocol(skb, skb->protocol); | ||
152 | skb->protocol = mpls->mpls_ethertype; | ||
153 | |||
154 | return 0; | ||
155 | } | ||
156 | |||
157 | static int pop_mpls(struct sk_buff *skb, const __be16 ethertype) | ||
158 | { | ||
159 | struct ethhdr *hdr; | ||
160 | int err; | ||
161 | |||
162 | err = make_writable(skb, skb->mac_len + MPLS_HLEN); | ||
163 | if (unlikely(err)) | ||
164 | return err; | ||
165 | |||
166 | if (skb->ip_summed == CHECKSUM_COMPLETE) | ||
167 | skb->csum = csum_sub(skb->csum, | ||
168 | csum_partial(skb_mpls_header(skb), | ||
169 | MPLS_HLEN, 0)); | ||
170 | |||
171 | memmove(skb_mac_header(skb) + MPLS_HLEN, skb_mac_header(skb), | ||
172 | skb->mac_len); | ||
173 | |||
174 | __skb_pull(skb, MPLS_HLEN); | ||
175 | skb_reset_mac_header(skb); | ||
176 | |||
177 | /* skb_mpls_header() is used to locate the ethertype | ||
178 | * field correctly in the presence of VLAN tags. | ||
179 | */ | ||
180 | hdr = (struct ethhdr *)(skb_mpls_header(skb) - ETH_HLEN); | ||
181 | hdr->h_proto = ethertype; | ||
182 | if (eth_p_mpls(skb->protocol)) | ||
183 | skb->protocol = ethertype; | ||
184 | return 0; | ||
185 | } | ||
186 | |||
187 | static int set_mpls(struct sk_buff *skb, const __be32 *mpls_lse) | ||
188 | { | ||
189 | __be32 *stack; | ||
190 | int err; | ||
191 | |||
192 | err = make_writable(skb, skb->mac_len + MPLS_HLEN); | ||
193 | if (unlikely(err)) | ||
194 | return err; | ||
195 | |||
196 | stack = (__be32 *)skb_mpls_header(skb); | ||
197 | if (skb->ip_summed == CHECKSUM_COMPLETE) { | ||
198 | __be32 diff[] = { ~(*stack), *mpls_lse }; | ||
199 | |||
200 | skb->csum = ~csum_partial((char *)diff, sizeof(diff), | ||
201 | ~skb->csum); | ||
202 | } | ||
203 | |||
204 | *stack = *mpls_lse; | ||
205 | |||
206 | return 0; | ||
207 | } | ||
208 | |||
121 | /* remove VLAN header from packet and update csum accordingly. */ | 209 | /* remove VLAN header from packet and update csum accordingly. */ |
122 | static int __pop_vlan_tci(struct sk_buff *skb, __be16 *current_tci) | 210 | static int __pop_vlan_tci(struct sk_buff *skb, __be16 *current_tci) |
123 | { | 211 | { |
@@ -140,10 +228,12 @@ static int __pop_vlan_tci(struct sk_buff *skb, __be16 *current_tci) | |||
140 | 228 | ||
141 | vlan_set_encap_proto(skb, vhdr); | 229 | vlan_set_encap_proto(skb, vhdr); |
142 | skb->mac_header += VLAN_HLEN; | 230 | skb->mac_header += VLAN_HLEN; |
231 | |||
143 | if (skb_network_offset(skb) < ETH_HLEN) | 232 | if (skb_network_offset(skb) < ETH_HLEN) |
144 | skb_set_network_header(skb, ETH_HLEN); | 233 | skb_set_network_header(skb, ETH_HLEN); |
145 | skb_reset_mac_len(skb); | ||
146 | 234 | ||
235 | /* Update mac_len for subsequent MPLS actions */ | ||
236 | skb_reset_mac_len(skb); | ||
147 | return 0; | 237 | return 0; |
148 | } | 238 | } |
149 | 239 | ||
@@ -186,6 +276,8 @@ static int push_vlan(struct sk_buff *skb, const struct ovs_action_push_vlan *vla | |||
186 | 276 | ||
187 | if (!__vlan_put_tag(skb, skb->vlan_proto, current_tag)) | 277 | if (!__vlan_put_tag(skb, skb->vlan_proto, current_tag)) |
188 | return -ENOMEM; | 278 | return -ENOMEM; |
279 | /* Update mac_len for subsequent MPLS actions */ | ||
280 | skb->mac_len += VLAN_HLEN; | ||
189 | 281 | ||
190 | if (skb->ip_summed == CHECKSUM_COMPLETE) | 282 | if (skb->ip_summed == CHECKSUM_COMPLETE) |
191 | skb->csum = csum_add(skb->csum, csum_partial(skb->data | 283 | skb->csum = csum_add(skb->csum, csum_partial(skb->data |
@@ -612,6 +704,10 @@ static int execute_set_action(struct sk_buff *skb, | |||
612 | case OVS_KEY_ATTR_SCTP: | 704 | case OVS_KEY_ATTR_SCTP: |
613 | err = set_sctp(skb, nla_data(nested_attr)); | 705 | err = set_sctp(skb, nla_data(nested_attr)); |
614 | break; | 706 | break; |
707 | |||
708 | case OVS_KEY_ATTR_MPLS: | ||
709 | err = set_mpls(skb, nla_data(nested_attr)); | ||
710 | break; | ||
615 | } | 711 | } |
616 | 712 | ||
617 | return err; | 713 | return err; |
@@ -690,6 +786,14 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, | |||
690 | execute_hash(skb, key, a); | 786 | execute_hash(skb, key, a); |
691 | break; | 787 | break; |
692 | 788 | ||
789 | case OVS_ACTION_ATTR_PUSH_MPLS: | ||
790 | err = push_mpls(skb, nla_data(a)); | ||
791 | break; | ||
792 | |||
793 | case OVS_ACTION_ATTR_POP_MPLS: | ||
794 | err = pop_mpls(skb, nla_get_be16(a)); | ||
795 | break; | ||
796 | |||
693 | case OVS_ACTION_ATTR_PUSH_VLAN: | 797 | case OVS_ACTION_ATTR_PUSH_VLAN: |
694 | err = push_vlan(skb, nla_data(a)); | 798 | err = push_vlan(skb, nla_data(a)); |
695 | if (unlikely(err)) /* skb already freed. */ | 799 | if (unlikely(err)) /* skb already freed. */ |