1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2c411ed85SJiri Benc /* 3c411ed85SJiri Benc * Network Service Header 4c411ed85SJiri Benc * 5c411ed85SJiri Benc * Copyright (c) 2017 Red Hat, Inc. -- Jiri Benc <jbenc@redhat.com> 6c411ed85SJiri Benc */ 7c411ed85SJiri Benc 8c411ed85SJiri Benc #include <linux/module.h> 9c411ed85SJiri Benc #include <linux/netdevice.h> 10c411ed85SJiri Benc #include <linux/skbuff.h> 11d457a0e3SEric Dumazet #include <net/gso.h> 12c411ed85SJiri Benc #include <net/nsh.h> 13c411ed85SJiri Benc #include <net/tun_proto.h> 14c411ed85SJiri Benc 15b2d0f5d5SYi Yang int nsh_push(struct sk_buff *skb, const struct nshhdr *pushed_nh) 16b2d0f5d5SYi Yang { 17b2d0f5d5SYi Yang struct nshhdr *nh; 18b2d0f5d5SYi Yang size_t length = nsh_hdr_len(pushed_nh); 19b2d0f5d5SYi Yang u8 next_proto; 20b2d0f5d5SYi Yang 21b2d0f5d5SYi Yang if (skb->mac_len) { 22b2d0f5d5SYi Yang next_proto = TUN_P_ETHERNET; 23b2d0f5d5SYi Yang } else { 24b2d0f5d5SYi Yang next_proto = tun_p_from_eth_p(skb->protocol); 25b2d0f5d5SYi Yang if (!next_proto) 26b2d0f5d5SYi Yang return -EAFNOSUPPORT; 27b2d0f5d5SYi Yang } 28b2d0f5d5SYi Yang 29b2d0f5d5SYi Yang /* Add the NSH header */ 30b2d0f5d5SYi Yang if (skb_cow_head(skb, length) < 0) 31b2d0f5d5SYi Yang return -ENOMEM; 32b2d0f5d5SYi Yang 33b2d0f5d5SYi Yang skb_push(skb, length); 34b2d0f5d5SYi Yang nh = (struct nshhdr *)(skb->data); 35b2d0f5d5SYi Yang memcpy(nh, pushed_nh, length); 36b2d0f5d5SYi Yang nh->np = next_proto; 37b2d0f5d5SYi Yang skb_postpush_rcsum(skb, nh, length); 38b2d0f5d5SYi Yang 39b2d0f5d5SYi Yang skb->protocol = htons(ETH_P_NSH); 40b2d0f5d5SYi Yang skb_reset_mac_header(skb); 41b2d0f5d5SYi Yang skb_reset_network_header(skb); 42b2d0f5d5SYi Yang skb_reset_mac_len(skb); 43b2d0f5d5SYi Yang 44b2d0f5d5SYi Yang return 0; 45b2d0f5d5SYi Yang } 46b2d0f5d5SYi Yang EXPORT_SYMBOL_GPL(nsh_push); 47b2d0f5d5SYi Yang 48b2d0f5d5SYi Yang int nsh_pop(struct sk_buff *skb) 49b2d0f5d5SYi Yang { 50b2d0f5d5SYi Yang struct nshhdr *nh; 51b2d0f5d5SYi Yang size_t length; 52b2d0f5d5SYi Yang __be16 inner_proto; 53b2d0f5d5SYi Yang 54b2d0f5d5SYi Yang if (!pskb_may_pull(skb, NSH_BASE_HDR_LEN)) 55b2d0f5d5SYi Yang return -ENOMEM; 56b2d0f5d5SYi Yang nh = (struct nshhdr *)(skb->data); 57b2d0f5d5SYi Yang length = nsh_hdr_len(nh); 58af50e4baSEric Dumazet if (length < NSH_BASE_HDR_LEN) 59af50e4baSEric Dumazet return -EINVAL; 60b2d0f5d5SYi Yang inner_proto = tun_p_to_eth_p(nh->np); 61b2d0f5d5SYi Yang if (!pskb_may_pull(skb, length)) 62b2d0f5d5SYi Yang return -ENOMEM; 63b2d0f5d5SYi Yang 64b2d0f5d5SYi Yang if (!inner_proto) 65b2d0f5d5SYi Yang return -EAFNOSUPPORT; 66b2d0f5d5SYi Yang 67b2d0f5d5SYi Yang skb_pull_rcsum(skb, length); 68b2d0f5d5SYi Yang skb_reset_mac_header(skb); 69b2d0f5d5SYi Yang skb_reset_network_header(skb); 70b2d0f5d5SYi Yang skb_reset_mac_len(skb); 71b2d0f5d5SYi Yang skb->protocol = inner_proto; 72b2d0f5d5SYi Yang 73b2d0f5d5SYi Yang return 0; 74b2d0f5d5SYi Yang } 75b2d0f5d5SYi Yang EXPORT_SYMBOL_GPL(nsh_pop); 76b2d0f5d5SYi Yang 77c411ed85SJiri Benc static struct sk_buff *nsh_gso_segment(struct sk_buff *skb, 78c411ed85SJiri Benc netdev_features_t features) 79c411ed85SJiri Benc { 80*696d18bbSKuniyuki Iwashima unsigned int outer_hlen, mac_len, nsh_len; 81c411ed85SJiri Benc struct sk_buff *segs = ERR_PTR(-EINVAL); 82c83b4938SDong Chenchen u16 mac_offset = skb->mac_header; 83*696d18bbSKuniyuki Iwashima __be16 outer_proto, proto; 84c411ed85SJiri Benc 85c411ed85SJiri Benc skb_reset_network_header(skb); 86c411ed85SJiri Benc 87*696d18bbSKuniyuki Iwashima outer_proto = skb->protocol; 88*696d18bbSKuniyuki Iwashima outer_hlen = skb_mac_header_len(skb); 89c411ed85SJiri Benc mac_len = skb->mac_len; 90c411ed85SJiri Benc 91c411ed85SJiri Benc if (unlikely(!pskb_may_pull(skb, NSH_BASE_HDR_LEN))) 92c411ed85SJiri Benc goto out; 93c411ed85SJiri Benc nsh_len = nsh_hdr_len(nsh_hdr(skb)); 94af50e4baSEric Dumazet if (nsh_len < NSH_BASE_HDR_LEN) 95af50e4baSEric Dumazet goto out; 96c411ed85SJiri Benc if (unlikely(!pskb_may_pull(skb, nsh_len))) 97c411ed85SJiri Benc goto out; 98c411ed85SJiri Benc 99c411ed85SJiri Benc proto = tun_p_to_eth_p(nsh_hdr(skb)->np); 100c411ed85SJiri Benc if (!proto) 101c411ed85SJiri Benc goto out; 102c411ed85SJiri Benc 103c411ed85SJiri Benc __skb_pull(skb, nsh_len); 104c411ed85SJiri Benc 105c411ed85SJiri Benc skb_reset_mac_header(skb); 106bab2c80eSWillem de Bruijn skb->mac_len = proto == htons(ETH_P_TEB) ? ETH_HLEN : 0; 107c411ed85SJiri Benc skb->protocol = proto; 108c411ed85SJiri Benc 109c411ed85SJiri Benc features &= NETIF_F_SG; 110c411ed85SJiri Benc segs = skb_mac_gso_segment(skb, features); 111c411ed85SJiri Benc if (IS_ERR_OR_NULL(segs)) { 112c411ed85SJiri Benc skb_gso_error_unwind(skb, htons(ETH_P_NSH), nsh_len, 113c83b4938SDong Chenchen mac_offset, mac_len); 114c411ed85SJiri Benc goto out; 115c411ed85SJiri Benc } 116c411ed85SJiri Benc 117c411ed85SJiri Benc for (skb = segs; skb; skb = skb->next) { 118*696d18bbSKuniyuki Iwashima skb->protocol = outer_proto; 119*696d18bbSKuniyuki Iwashima __skb_push(skb, nsh_len + outer_hlen); 120*696d18bbSKuniyuki Iwashima skb_reset_mac_header(skb); 121*696d18bbSKuniyuki Iwashima skb_set_network_header(skb, outer_hlen); 122c411ed85SJiri Benc skb->mac_len = mac_len; 123c411ed85SJiri Benc } 124c411ed85SJiri Benc 125c411ed85SJiri Benc out: 126c411ed85SJiri Benc return segs; 127c411ed85SJiri Benc } 128c411ed85SJiri Benc 129c411ed85SJiri Benc static struct packet_offload nsh_packet_offload __read_mostly = { 130c411ed85SJiri Benc .type = htons(ETH_P_NSH), 131c411ed85SJiri Benc .priority = 15, 132c411ed85SJiri Benc .callbacks = { 133c411ed85SJiri Benc .gso_segment = nsh_gso_segment, 134c411ed85SJiri Benc }, 135c411ed85SJiri Benc }; 136c411ed85SJiri Benc 137c411ed85SJiri Benc static int __init nsh_init_module(void) 138c411ed85SJiri Benc { 139c411ed85SJiri Benc dev_add_offload(&nsh_packet_offload); 140c411ed85SJiri Benc return 0; 141c411ed85SJiri Benc } 142c411ed85SJiri Benc 143c411ed85SJiri Benc static void __exit nsh_cleanup_module(void) 144c411ed85SJiri Benc { 145c411ed85SJiri Benc dev_remove_offload(&nsh_packet_offload); 146c411ed85SJiri Benc } 147c411ed85SJiri Benc 148c411ed85SJiri Benc module_init(nsh_init_module); 149c411ed85SJiri Benc module_exit(nsh_cleanup_module); 150c411ed85SJiri Benc 151c411ed85SJiri Benc MODULE_AUTHOR("Jiri Benc <jbenc@redhat.com>"); 152c411ed85SJiri Benc MODULE_DESCRIPTION("NSH protocol"); 153c411ed85SJiri Benc MODULE_LICENSE("GPL v2"); 154