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