Blame view

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