Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/net/caif/cfpkt_skbuff.c
26278 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
/*
3
* Copyright (C) ST-Ericsson AB 2010
4
* Author: Sjur Brendeland
5
*/
6
7
#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__
8
9
#include <linux/string.h>
10
#include <linux/skbuff.h>
11
#include <linux/export.h>
12
#include <net/caif/cfpkt.h>
13
14
#define PKT_PREFIX 48
15
#define PKT_POSTFIX 2
16
#define PKT_LEN_WHEN_EXTENDING 128
17
#define PKT_ERROR(pkt, errmsg) \
18
do { \
19
cfpkt_priv(pkt)->erronous = true; \
20
skb_reset_tail_pointer(&pkt->skb); \
21
pr_warn(errmsg); \
22
} while (0)
23
24
/*
25
* net/caif/ is generic and does not
26
* understand SKB, so we do this typecast
27
*/
28
struct cfpkt {
29
struct sk_buff skb;
30
};
31
32
/* Private data inside SKB */
33
struct cfpkt_priv_data {
34
struct dev_info dev_info;
35
bool erronous;
36
};
37
38
static inline struct cfpkt_priv_data *cfpkt_priv(struct cfpkt *pkt)
39
{
40
return (struct cfpkt_priv_data *) pkt->skb.cb;
41
}
42
43
static inline bool is_erronous(struct cfpkt *pkt)
44
{
45
return cfpkt_priv(pkt)->erronous;
46
}
47
48
static inline struct sk_buff *pkt_to_skb(struct cfpkt *pkt)
49
{
50
return &pkt->skb;
51
}
52
53
static inline struct cfpkt *skb_to_pkt(struct sk_buff *skb)
54
{
55
return (struct cfpkt *) skb;
56
}
57
58
struct cfpkt *cfpkt_fromnative(enum caif_direction dir, void *nativepkt)
59
{
60
struct cfpkt *pkt = skb_to_pkt(nativepkt);
61
cfpkt_priv(pkt)->erronous = false;
62
return pkt;
63
}
64
EXPORT_SYMBOL(cfpkt_fromnative);
65
66
void *cfpkt_tonative(struct cfpkt *pkt)
67
{
68
return (void *) pkt;
69
}
70
EXPORT_SYMBOL(cfpkt_tonative);
71
72
static struct cfpkt *cfpkt_create_pfx(u16 len, u16 pfx)
73
{
74
struct sk_buff *skb;
75
76
skb = alloc_skb(len + pfx, GFP_ATOMIC);
77
if (unlikely(skb == NULL))
78
return NULL;
79
80
skb_reserve(skb, pfx);
81
return skb_to_pkt(skb);
82
}
83
84
inline struct cfpkt *cfpkt_create(u16 len)
85
{
86
return cfpkt_create_pfx(len + PKT_POSTFIX, PKT_PREFIX);
87
}
88
89
void cfpkt_destroy(struct cfpkt *pkt)
90
{
91
struct sk_buff *skb = pkt_to_skb(pkt);
92
kfree_skb(skb);
93
}
94
95
inline bool cfpkt_more(struct cfpkt *pkt)
96
{
97
struct sk_buff *skb = pkt_to_skb(pkt);
98
return skb->len > 0;
99
}
100
101
int cfpkt_peek_head(struct cfpkt *pkt, void *data, u16 len)
102
{
103
struct sk_buff *skb = pkt_to_skb(pkt);
104
if (skb_headlen(skb) >= len) {
105
memcpy(data, skb->data, len);
106
return 0;
107
}
108
return !cfpkt_extr_head(pkt, data, len) &&
109
!cfpkt_add_head(pkt, data, len);
110
}
111
112
int cfpkt_extr_head(struct cfpkt *pkt, void *data, u16 len)
113
{
114
struct sk_buff *skb = pkt_to_skb(pkt);
115
u8 *from;
116
if (unlikely(is_erronous(pkt)))
117
return -EPROTO;
118
119
if (unlikely(len > skb->len)) {
120
PKT_ERROR(pkt, "read beyond end of packet\n");
121
return -EPROTO;
122
}
123
124
if (unlikely(len > skb_headlen(skb))) {
125
if (unlikely(skb_linearize(skb) != 0)) {
126
PKT_ERROR(pkt, "linearize failed\n");
127
return -EPROTO;
128
}
129
}
130
from = skb_pull(skb, len);
131
from -= len;
132
if (data)
133
memcpy(data, from, len);
134
return 0;
135
}
136
EXPORT_SYMBOL(cfpkt_extr_head);
137
138
int cfpkt_extr_trail(struct cfpkt *pkt, void *dta, u16 len)
139
{
140
struct sk_buff *skb = pkt_to_skb(pkt);
141
u8 *data = dta;
142
u8 *from;
143
if (unlikely(is_erronous(pkt)))
144
return -EPROTO;
145
146
if (unlikely(skb_linearize(skb) != 0)) {
147
PKT_ERROR(pkt, "linearize failed\n");
148
return -EPROTO;
149
}
150
if (unlikely(skb->data + len > skb_tail_pointer(skb))) {
151
PKT_ERROR(pkt, "read beyond end of packet\n");
152
return -EPROTO;
153
}
154
from = skb_tail_pointer(skb) - len;
155
skb_trim(skb, skb->len - len);
156
memcpy(data, from, len);
157
return 0;
158
}
159
160
int cfpkt_pad_trail(struct cfpkt *pkt, u16 len)
161
{
162
return cfpkt_add_body(pkt, NULL, len);
163
}
164
165
int cfpkt_add_body(struct cfpkt *pkt, const void *data, u16 len)
166
{
167
struct sk_buff *skb = pkt_to_skb(pkt);
168
struct sk_buff *lastskb;
169
u8 *to;
170
u16 addlen = 0;
171
172
173
if (unlikely(is_erronous(pkt)))
174
return -EPROTO;
175
176
lastskb = skb;
177
178
/* Check whether we need to add space at the tail */
179
if (unlikely(skb_tailroom(skb) < len)) {
180
if (likely(len < PKT_LEN_WHEN_EXTENDING))
181
addlen = PKT_LEN_WHEN_EXTENDING;
182
else
183
addlen = len;
184
}
185
186
/* Check whether we need to change the SKB before writing to the tail */
187
if (unlikely((addlen > 0) || skb_cloned(skb) || skb_shared(skb))) {
188
189
/* Make sure data is writable */
190
if (unlikely(skb_cow_data(skb, addlen, &lastskb) < 0)) {
191
PKT_ERROR(pkt, "cow failed\n");
192
return -EPROTO;
193
}
194
}
195
196
/* All set to put the last SKB and optionally write data there. */
197
to = pskb_put(skb, lastskb, len);
198
if (likely(data))
199
memcpy(to, data, len);
200
return 0;
201
}
202
203
inline int cfpkt_addbdy(struct cfpkt *pkt, u8 data)
204
{
205
return cfpkt_add_body(pkt, &data, 1);
206
}
207
208
int cfpkt_add_head(struct cfpkt *pkt, const void *data2, u16 len)
209
{
210
struct sk_buff *skb = pkt_to_skb(pkt);
211
struct sk_buff *lastskb;
212
u8 *to;
213
const u8 *data = data2;
214
int ret;
215
if (unlikely(is_erronous(pkt)))
216
return -EPROTO;
217
if (unlikely(skb_headroom(skb) < len)) {
218
PKT_ERROR(pkt, "no headroom\n");
219
return -EPROTO;
220
}
221
222
/* Make sure data is writable */
223
ret = skb_cow_data(skb, 0, &lastskb);
224
if (unlikely(ret < 0)) {
225
PKT_ERROR(pkt, "cow failed\n");
226
return ret;
227
}
228
229
to = skb_push(skb, len);
230
memcpy(to, data, len);
231
return 0;
232
}
233
EXPORT_SYMBOL(cfpkt_add_head);
234
235
inline int cfpkt_add_trail(struct cfpkt *pkt, const void *data, u16 len)
236
{
237
return cfpkt_add_body(pkt, data, len);
238
}
239
240
inline u16 cfpkt_getlen(struct cfpkt *pkt)
241
{
242
struct sk_buff *skb = pkt_to_skb(pkt);
243
return skb->len;
244
}
245
246
int cfpkt_iterate(struct cfpkt *pkt,
247
u16 (*iter_func)(u16, void *, u16),
248
u16 data)
249
{
250
/*
251
* Don't care about the performance hit of linearizing,
252
* Checksum should not be used on high-speed interfaces anyway.
253
*/
254
if (unlikely(is_erronous(pkt)))
255
return -EPROTO;
256
if (unlikely(skb_linearize(&pkt->skb) != 0)) {
257
PKT_ERROR(pkt, "linearize failed\n");
258
return -EPROTO;
259
}
260
return iter_func(data, pkt->skb.data, cfpkt_getlen(pkt));
261
}
262
263
int cfpkt_setlen(struct cfpkt *pkt, u16 len)
264
{
265
struct sk_buff *skb = pkt_to_skb(pkt);
266
267
268
if (unlikely(is_erronous(pkt)))
269
return -EPROTO;
270
271
if (likely(len <= skb->len)) {
272
if (unlikely(skb->data_len))
273
___pskb_trim(skb, len);
274
else
275
skb_trim(skb, len);
276
277
return cfpkt_getlen(pkt);
278
}
279
280
/* Need to expand SKB */
281
if (unlikely(!cfpkt_pad_trail(pkt, len - skb->len)))
282
PKT_ERROR(pkt, "skb_pad_trail failed\n");
283
284
return cfpkt_getlen(pkt);
285
}
286
287
struct cfpkt *cfpkt_append(struct cfpkt *dstpkt,
288
struct cfpkt *addpkt,
289
u16 expectlen)
290
{
291
struct sk_buff *dst = pkt_to_skb(dstpkt);
292
struct sk_buff *add = pkt_to_skb(addpkt);
293
u16 addlen = skb_headlen(add);
294
u16 neededtailspace;
295
struct sk_buff *tmp;
296
u16 dstlen;
297
u16 createlen;
298
if (unlikely(is_erronous(dstpkt) || is_erronous(addpkt))) {
299
return dstpkt;
300
}
301
302
neededtailspace = max(expectlen, addlen);
303
304
if (dst->tail + neededtailspace > dst->end) {
305
/* Create a dumplicate of 'dst' with more tail space */
306
struct cfpkt *tmppkt;
307
dstlen = skb_headlen(dst);
308
createlen = dstlen + neededtailspace;
309
tmppkt = cfpkt_create(createlen + PKT_PREFIX + PKT_POSTFIX);
310
if (tmppkt == NULL)
311
return NULL;
312
tmp = pkt_to_skb(tmppkt);
313
skb_put_data(tmp, dst->data, dstlen);
314
cfpkt_destroy(dstpkt);
315
dst = tmp;
316
}
317
skb_put_data(dst, add->data, skb_headlen(add));
318
cfpkt_destroy(addpkt);
319
return skb_to_pkt(dst);
320
}
321
322
struct cfpkt *cfpkt_split(struct cfpkt *pkt, u16 pos)
323
{
324
struct sk_buff *skb2;
325
struct sk_buff *skb = pkt_to_skb(pkt);
326
struct cfpkt *tmppkt;
327
u8 *split = skb->data + pos;
328
u16 len2nd = skb_tail_pointer(skb) - split;
329
330
if (unlikely(is_erronous(pkt)))
331
return NULL;
332
333
if (skb->data + pos > skb_tail_pointer(skb)) {
334
PKT_ERROR(pkt, "trying to split beyond end of packet\n");
335
return NULL;
336
}
337
338
/* Create a new packet for the second part of the data */
339
tmppkt = cfpkt_create_pfx(len2nd + PKT_PREFIX + PKT_POSTFIX,
340
PKT_PREFIX);
341
if (tmppkt == NULL)
342
return NULL;
343
skb2 = pkt_to_skb(tmppkt);
344
345
346
if (skb2 == NULL)
347
return NULL;
348
349
skb_put_data(skb2, split, len2nd);
350
351
/* Reduce the length of the original packet */
352
skb_trim(skb, pos);
353
354
skb2->priority = skb->priority;
355
return skb_to_pkt(skb2);
356
}
357
358
bool cfpkt_erroneous(struct cfpkt *pkt)
359
{
360
return cfpkt_priv(pkt)->erronous;
361
}
362
363
struct caif_payload_info *cfpkt_info(struct cfpkt *pkt)
364
{
365
return (struct caif_payload_info *)&pkt_to_skb(pkt)->cb;
366
}
367
EXPORT_SYMBOL(cfpkt_info);
368
369
void cfpkt_set_prio(struct cfpkt *pkt, int prio)
370
{
371
pkt_to_skb(pkt)->priority = prio;
372
}
373
EXPORT_SYMBOL(cfpkt_set_prio);
374
375