nlmsg: add lacking attributes validation
[platform/upstream/libnetfilter_queue.git] / src / extra / pktbuff.c
1 /*
2  * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This code has been sponsored by Vyatta Inc. <http://www.vyatta.com>
10  */
11
12 #include <stdlib.h>
13 #include <string.h> /* for memcpy */
14 #include <stdbool.h>
15
16 #include <netinet/if_ether.h>
17 #include <netinet/ip.h>
18 #include <netinet/tcp.h>
19
20 #include "internal.h"
21
22 /**
23  * \defgroup pktbuff User-space network packet buffer
24  *
25  * This library provides the user-space network packet buffer. This abstraction
26  * is strongly inspired by Linux kernel network buffer, the so-called sk_buff.
27  *
28  * @{
29  */
30
31 /**
32  * pktb_alloc - allocate a new packet buffer
33  * \param family Indicate what family, eg. AF_BRIDGE, AF_INET, AF_INET6, ...
34  * \param data Pointer to packet data
35  * \param len Packet length
36  * \param extra Extra memory in the tail to be allocated (for mangling)
37  *
38  * This function returns a packet buffer that contains the packet data and
39  * some extra memory room in the tail (in case of requested).
40  *
41  * \return a pointer to a new queue handle or NULL on failure.
42  */
43 struct pkt_buff *
44 pktb_alloc(int family, void *data, size_t len, size_t extra)
45 {
46         struct pkt_buff *pktb;
47         void *pkt_data;
48
49         pktb = calloc(1, sizeof(struct pkt_buff) + len + extra);
50         if (pktb == NULL)
51                 return NULL;
52
53         /* Better make sure alignment is correct. */
54         pkt_data = (uint8_t *)pktb + sizeof(struct pkt_buff);
55         memcpy(pkt_data, data, len);
56
57         pktb->len = len;
58         pktb->data_len = len + extra;
59
60         pktb->head = pkt_data;
61         pktb->data = pkt_data;
62         pktb->tail = pktb->head + len;
63
64         switch(family) {
65         case AF_INET:
66                 pktb->network_header = pktb->data;
67                 break;
68         case AF_BRIDGE: {
69                 struct ethhdr *ethhdr = (struct ethhdr *)pktb->data;
70
71                 pktb->mac_header = pktb->data;
72
73                 switch(ethhdr->h_proto) {
74                 case ETH_P_IP:
75                         pktb->network_header = pktb->data + ETH_HLEN;
76                         break;
77                 default:
78                         /* This protocol is unsupported. */
79                         free(pktb);
80                         return NULL;
81                 }
82                 break;
83         }
84         }
85         return pktb;
86 }
87 EXPORT_SYMBOL(pktb_alloc);
88
89 /**
90  * pktb_data - return pointer to the beginning of the packet buffer
91  * \param pktb Pointer to packet buffer
92  */
93 uint8_t *pktb_data(struct pkt_buff *pktb)
94 {
95         return pktb->data;
96 }
97 EXPORT_SYMBOL(pktb_data);
98
99 /**
100  * pktb_len - return length of the packet buffer
101  * \param pktb Pointer to packet buffer
102  */
103 uint32_t pktb_len(struct pkt_buff *pktb)
104 {
105         return pktb->len;
106 }
107 EXPORT_SYMBOL(pktb_len);
108
109 /**
110  * pktb_free - release packet buffer
111  * \param pktb Pointer to packet buffer
112  */
113 void pktb_free(struct pkt_buff *pktb)
114 {
115         free(pktb);
116 }
117 EXPORT_SYMBOL(pktb_free);
118
119 /**
120  * pktb_push - update pointer to the beginning of the packet buffer
121  * \param pktb Pointer to packet buffer
122  */
123 void pktb_push(struct pkt_buff *pktb, unsigned int len)
124 {
125         pktb->data -= len;
126         pktb->len += len;
127 }
128 EXPORT_SYMBOL(pktb_push);
129
130 /**
131  * pktb_pull - update pointer to the beginning of the packet buffer
132  * \param pktb Pointer to packet buffer
133  */
134 void pktb_pull(struct pkt_buff *pktb, unsigned int len)
135 {
136         pktb->data += len;
137         pktb->len -= len;
138 }
139 EXPORT_SYMBOL(pktb_pull);
140
141 /**
142  * pktb_put - add extra bytes to the tail of the packet buffer
143  * \param pktb Pointer to packet buffer
144  */
145 void pktb_put(struct pkt_buff *pktb, unsigned int len)
146 {
147         pktb->tail += len;
148         pktb->len += len;
149 }
150 EXPORT_SYMBOL(pktb_put);
151
152 /**
153  * pktb_trim - set new length for this packet buffer
154  * \param pktb Pointer to packet buffer
155  */
156 void pktb_trim(struct pkt_buff *pktb, unsigned int len)
157 {
158         pktb->len = len;
159 }
160 EXPORT_SYMBOL(pktb_trim);
161
162 /**
163  * pktb_tailroom - get room in bytes in the tail of the packet buffer
164  * \param pktb Pointer to packet buffer
165  */
166 unsigned int pktb_tailroom(struct pkt_buff *pktb)
167 {
168         return pktb->data_len - pktb->len;
169 }
170 EXPORT_SYMBOL(pktb_tailroom);
171
172 /**
173  * pktb_mac_header - return pointer to layer 2 header (if any)
174  * \param pktb Pointer to packet buffer
175  */
176 uint8_t *pktb_mac_header(struct pkt_buff *pktb)
177 {
178         return pktb->mac_header;
179 }
180 EXPORT_SYMBOL(pktb_mac_header);
181
182 /**
183  * pktb_network_header - return pointer to layer 3 header
184  * \param pktb Pointer to packet buffer
185  */
186 uint8_t *pktb_network_header(struct pkt_buff *pktb)
187 {
188         return pktb->network_header;
189 }
190 EXPORT_SYMBOL(pktb_network_header);
191
192 /**
193  * pktb_transport_header - return pointer to layer 4 header (if any)
194  * \param pktb Pointer to packet buffer
195  */
196 uint8_t *pktb_transport_header(struct pkt_buff *pktb)
197 {
198         return pktb->transport_header;
199 }
200 EXPORT_SYMBOL(pktb_transport_header);
201
202 static int pktb_expand_tail(struct pkt_buff *pkt, int extra)
203 {
204         /* No room in packet, cannot mangle it. We don't support dynamic
205          * reallocation. Instead, increase the size of the extra room in
206          * the tail in pktb_alloc.
207          */
208         if (pkt->len + extra > pkt->data_len)
209                 return 0;
210
211         pkt->len += extra;
212         pkt->tail = pkt->tail + extra;
213         return 1;
214 }
215
216 static int enlarge_pkt(struct pkt_buff *pkt, unsigned int extra)
217 {
218         if (pkt->len + extra > 65535)
219                 return 0;
220
221         if (!pktb_expand_tail(pkt, extra - pktb_tailroom(pkt)))
222                 return 0;
223
224         return 1;
225 }
226
227 int pktb_mangle(struct pkt_buff *pkt,
228                  unsigned int dataoff,
229                  unsigned int match_offset,
230                  unsigned int match_len,
231                  const char *rep_buffer,
232                  unsigned int rep_len)
233 {
234         unsigned char *data;
235
236         if (rep_len > match_len &&
237             rep_len - match_len > pktb_tailroom(pkt) &&
238             !enlarge_pkt(pkt, rep_len - match_len))
239                 return 0;
240
241         data = pkt->network_header + dataoff;
242
243         /* move post-replacement */
244         memmove(data + match_offset + rep_len,
245                 data + match_offset + match_len,
246                 pkt->tail - (pkt->network_header + dataoff +
247                              match_offset + match_len));
248
249         /* insert data from buffer */
250         memcpy(data + match_offset, rep_buffer, rep_len);
251
252         /* update pkt info */
253         if (rep_len > match_len)
254                 pktb_put(pkt, rep_len - match_len);
255         else
256                 pktb_trim(pkt, pkt->len + rep_len - match_len);
257
258         pkt->mangled = true;
259         return 1;
260 }
261 EXPORT_SYMBOL(pktb_mangle);
262
263 /**
264  * pktb_mangled - return true if packet has been mangled
265  * \param pktb Pointer to packet buffer
266  */
267 bool pktb_mangled(const struct pkt_buff *pkt)
268 {
269         return pkt->mangled;
270 }
271 EXPORT_SYMBOL(pktb_mangled);
272
273 /**
274  * @}
275  */