summaryrefslogtreecommitdiff
path: root/datapath/linux/compat/gso.h
blob: b0c5c17b74d7651356d300e09ac3746d3b46dde2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
#ifndef __LINUX_GSO_WRAPPER_H
#define __LINUX_GSO_WRAPPER_H

#include <linux/version.h>
#include "datapath.h"

typedef void (*gso_fix_segment_t)(struct sk_buff *);

struct ovs_gso_cb {
	struct ovs_skb_cb dp_cb;
#ifndef USE_UPSTREAM_TUNNEL
	struct metadata_dst	*tun_dst;
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,18,0)
	gso_fix_segment_t fix_segment;
#endif
#ifndef HAVE_INNER_PROTOCOL
	__be16		inner_protocol;
#endif
#ifndef HAVE_INNER_MAC_HEADER
	unsigned int	inner_mac_header;
#endif
#ifndef HAVE_INNER_NETWORK_HEADER
	unsigned int	inner_network_header;
#endif
#ifndef HAVE_NDO_FILL_METADATA_DST
	/* Keep original tunnel info during userspace action execution. */
	struct metadata_dst *fill_md_dst;
#endif

};
#define OVS_GSO_CB(skb) ((struct ovs_gso_cb *)(skb)->cb)


#if LINUX_VERSION_CODE < KERNEL_VERSION(3,18,0)
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <net/protocol.h>

static inline void skb_clear_ovs_gso_cb(struct sk_buff *skb)
{
	OVS_GSO_CB(skb)->fix_segment = NULL;
}
#else
static inline void skb_clear_ovs_gso_cb(struct sk_buff *skb)
{

}
#endif

#ifndef HAVE_INNER_MAC_HEADER
static inline unsigned char *skb_inner_mac_header(const struct sk_buff *skb)
{
	return skb->head + OVS_GSO_CB(skb)->inner_mac_header;
}

static inline void skb_set_inner_mac_header(const struct sk_buff *skb,
					    int offset)
{
	OVS_GSO_CB(skb)->inner_mac_header = (skb->data - skb->head) + offset;
}
#endif /* HAVE_INNER_MAC_HEADER */

#ifndef HAVE_INNER_NETWORK_HEADER
static inline unsigned char *skb_inner_network_header(const struct sk_buff *skb)
{
	return skb->head + OVS_GSO_CB(skb)->inner_network_header;
}

static inline int skb_inner_network_offset(const struct sk_buff *skb)
{
	return skb_inner_network_header(skb) - skb->data;
}

/* We don't actually store the transport offset on backports because
 * we don't use it anywhere. Slightly rename this version to avoid
 * future users from picking it up accidentially.
 */
static inline int ovs_skb_inner_transport_offset(const struct sk_buff *skb)
{
	return 0;
}

static inline void skb_set_inner_network_header(const struct sk_buff *skb,
						int offset)
{
	OVS_GSO_CB(skb)->inner_network_header = (skb->data - skb->head)
						+ offset;
}

static inline void skb_set_inner_transport_header(const struct sk_buff *skb,
						  int offset)
{ }

#else

static inline int ovs_skb_inner_transport_offset(const struct sk_buff *skb)
{
	return skb_inner_transport_header(skb) - skb->data;
}

#endif /* HAVE_INNER_NETWORK_HEADER */

#ifndef HAVE_INNER_PROTOCOL
static inline void ovs_skb_init_inner_protocol(struct sk_buff *skb)
{
	OVS_GSO_CB(skb)->inner_protocol = htons(0);
}

static inline void ovs_skb_set_inner_protocol(struct sk_buff *skb,
					      __be16 ethertype)
{
	OVS_GSO_CB(skb)->inner_protocol = ethertype;
}

static inline __be16 ovs_skb_get_inner_protocol(struct sk_buff *skb)
{
	return OVS_GSO_CB(skb)->inner_protocol;
}

#else

static inline void ovs_skb_init_inner_protocol(struct sk_buff *skb)
{
	/* Nothing to do. The inner_protocol is either zero or
	 * has been set to a value by another user.
	 * Either way it may be considered initialised.
	 */
}

static inline __be16 ovs_skb_get_inner_protocol(struct sk_buff *skb)
{
	return skb->inner_protocol;
}

#ifdef ENCAP_TYPE_ETHER
#define ovs_skb_set_inner_protocol skb_set_inner_protocol
#else
static inline void ovs_skb_set_inner_protocol(struct sk_buff *skb,
					      __be16 ethertype)
{
	skb->inner_protocol = ethertype;
}
#endif /* ENCAP_TYPE_ETHER */
#endif /* 3.11 */

#if LINUX_VERSION_CODE < KERNEL_VERSION(3,18,0)
#define ip_local_out rpl_ip_local_out
int rpl_ip_local_out(struct sk_buff *skb);

static inline int skb_inner_mac_offset(const struct sk_buff *skb)
{
	return skb_inner_mac_header(skb) - skb->data;
}

#define skb_reset_inner_headers rpl_skb_reset_inner_headers
static inline void skb_reset_inner_headers(struct sk_buff *skb)
{
	BUILD_BUG_ON(sizeof(struct ovs_gso_cb) > FIELD_SIZEOF(struct sk_buff, cb));
	skb_set_inner_mac_header(skb, skb_mac_header(skb) - skb->data);
	skb_set_inner_network_header(skb, skb_network_offset(skb));
	skb_set_inner_transport_header(skb, skb_transport_offset(skb));
}
#endif /* 3.18 */

#ifndef USE_UPSTREAM_TUNNEL
/* We need two separate functions to manage different dst in this case.
 * First is dst_entry and second is tunnel-dst.
 * So define ovs_* separate functions for tun_dst.
 */
static inline void ovs_skb_dst_set(struct sk_buff *skb, void *dst)
{
	OVS_GSO_CB(skb)->tun_dst = (void *)dst;
}

static inline struct ip_tunnel_info *ovs_skb_tunnel_info(struct sk_buff *skb)
{
	if (likely(OVS_GSO_CB(skb)->tun_dst))
		return &OVS_GSO_CB(skb)->tun_dst->u.tun_info;
	else
		return NULL;
}

static inline void ovs_skb_dst_drop(struct sk_buff *skb)
{
	OVS_GSO_CB(skb)->tun_dst = NULL;
}

static inline void ovs_dst_hold(void *dst)
{
}

static inline void ovs_dst_release(struct dst_entry *dst)
{
	kfree(dst);
}

#else
#define ovs_skb_dst_set skb_dst_set
#define ovs_skb_dst_drop skb_dst_drop
#define ovs_dst_hold dst_hold
#define ovs_dst_release dst_release
#endif

#ifndef HAVE_NDO_FILL_METADATA_DST
#define SKB_INIT_FILL_METADATA_DST(skb)	OVS_GSO_CB(skb)->fill_md_dst = NULL;

#define SKB_RESTORE_FILL_METADATA_DST(skb)	do {			\
	if (OVS_GSO_CB(skb)->fill_md_dst) {					\
		kfree(OVS_GSO_CB(skb)->tun_dst);			\
		OVS_GSO_CB(skb)->tun_dst = OVS_GSO_CB(skb)->fill_md_dst;	\
	}								\
} while (0)


#define SKB_SETUP_FILL_METADATA_DST(skb) ({			\
	struct metadata_dst *new_md_dst;			\
	struct metadata_dst *md_dst;				\
	int md_size;						\
	int ret = 1;						\
								\
	SKB_RESTORE_FILL_METADATA_DST(skb); 			\
	new_md_dst = kmalloc(sizeof(struct metadata_dst) + 256, GFP_ATOMIC); \
	if (new_md_dst) {						\
		md_dst = OVS_GSO_CB(skb)->tun_dst;			\
		md_size = new_md_dst->u.tun_info.options_len;		\
		memcpy(&new_md_dst->u.tun_info, &md_dst->u.tun_info,	\
			sizeof(struct ip_tunnel_info) + md_size);	\
									\
		OVS_GSO_CB(skb)->fill_md_dst = md_dst;				\
		OVS_GSO_CB(skb)->tun_dst = new_md_dst;			\
		ret = 1;						\
	} else {							\
		ret = 0;						\
	}								\
	ret;								\
})

#else
#define SKB_INIT_FILL_METADATA_DST(skb)		do {} while(0)
#define SKB_SETUP_FILL_METADATA_DST(skb)	(true)
#define SKB_RESTORE_FILL_METADATA_DST(skb)	do {} while(0)
#endif

#endif