| @@ -66,6 +66,7 @@ · 65 unmodified lines |
| 66 | 66 | #include <net/checksum.h> |
| 67 | 67 | #include <net/ip6_checksum.h> |
| 68 | 68 | #include <net/xfrm.h> |
| 69 | +#include <net/mpls.h> |
| 69 | 70 | |
| 70 | 71 | #include <linux/uaccess.h> |
| 71 | 72 | #include <trace/events/skb.h> |
| @@ -5425,6 +5426,69 @@ int skb_vlan_push(struct sk_buff *skb, __be16 vlan_proto, u16 vlan_tci) · 5353 unmodified lines |
| 5425 | 5426 | } |
| 5426 | 5427 | EXPORT_SYMBOL(skb_vlan_push); |
| 5427 | 5428 | |
| 5429 | +/* Update the ethertype of hdr and the skb csum value if required. */ |
| 5430 | +static void skb_mod_eth_type(struct sk_buff *skb, struct ethhdr *hdr, |
| 5431 | + __be16 ethertype) |
| 5432 | +{ |
| 5433 | + if (skb->ip_summed == CHECKSUM_COMPLETE) { |
| 5434 | + __be16 diff[] = { ~hdr->h_proto, ethertype }; |
| 5435 | + |
| 5436 | + skb->csum = csum_partial((char *)diff, sizeof(diff), skb->csum); |
| 5437 | + } |
| 5438 | + |
| 5439 | + hdr->h_proto = ethertype; |
| 5440 | +} |
| 5441 | + |
| 5442 | +/** |
| 5443 | + * skb_mpls_push() - push a new MPLS header after the mac header |
| 5444 | + * |
| 5445 | + * @skb: buffer |
| 5446 | + * @mpls_lse: MPLS label stack entry to push |
| 5447 | + * @mpls_proto: ethertype of the new MPLS header (expects 0x8847 or 0x8848) |
| 5448 | + * |
| 5449 | + * Expects skb->data at mac header. |
| 5450 | + * |
| 5451 | + * Returns 0 on success, -errno otherwise. |
| 5452 | + */ |
| 5453 | +int skb_mpls_push(struct sk_buff *skb, __be32 mpls_lse, __be16 mpls_proto) |
| 5454 | +{ |
| 5455 | + struct mpls_shim_hdr *lse; |
| 5456 | + int err; |
| 5457 | + |
| 5458 | + if (unlikely(!eth_p_mpls(mpls_proto))) |
| 5459 | + return -EINVAL; |
| 5460 | + |
| 5461 | + /* Networking stack does not allow simultaneous Tunnel and MPLS GSO. */ |
| 5462 | + if (skb->encapsulation) |
| 5463 | + return -EINVAL; |
| 5464 | + |
| 5465 | + err = skb_cow_head(skb, MPLS_HLEN); |
| 5466 | + if (unlikely(err)) |
| 5467 | + return err; |
| 5468 | + |
| 5469 | + if (!skb->inner_protocol) { |
| 5470 | + skb_set_inner_network_header(skb, skb->mac_len); |
| 5471 | + skb_set_inner_protocol(skb, skb->protocol); |
| 5472 | + } |
| 5473 | + |
| 5474 | + skb_push(skb, MPLS_HLEN); |
| 5475 | + memmove(skb_mac_header(skb) - MPLS_HLEN, skb_mac_header(skb), |
| 5476 | + skb->mac_len); |
| 5477 | + skb_reset_mac_header(skb); |
| 5478 | + skb_set_network_header(skb, skb->mac_len); |
| 5479 | + |
| 5480 | + lse = mpls_hdr(skb); |
| 5481 | + lse->label_stack_entry = mpls_lse; |
| 5482 | + skb_postpush_rcsum(skb, lse, MPLS_HLEN); |
| 5483 | + |
| 5484 | + if (skb->dev && skb->dev->type == ARPHRD_ETHER) |
| 5485 | + skb_mod_eth_type(skb, eth_hdr(skb), mpls_proto); |
| 5486 | + skb->protocol = mpls_proto; |
| 5487 | + |
| 5488 | + return 0; |
| 5489 | +} |
| 5490 | +EXPORT_SYMBOL_GPL(skb_mpls_push); |
| 5491 | + |
| 5428 | 5492 | /** |
| 5429 | 5493 | * alloc_skb_with_frags - allocate skb with page frags |
| 5430 | 5494 | * |