trafgen: l7: Add DNS header generation API
[netsniff-ng-new.git] / trafgen_l4.c
blob198d622e52e61b57a44c162068673042e46c5ca1
1 /*
2 * netsniff-ng - the packet sniffing beast
3 * Subject to the GPL, version 2.
4 */
6 #include <stdbool.h>
7 #include <netinet/in.h>
9 #include "die.h"
10 #include "csum.h"
11 #include "built_in.h"
12 #include "trafgen_l3.h"
13 #include "trafgen_l4.h"
14 #include "trafgen_conf.h"
15 #include "trafgen_proto.h"
17 static struct proto_field udp_fields[] = {
18 { .id = UDP_SPORT, .len = 2, .offset = 0 },
19 { .id = UDP_DPORT, .len = 2, .offset = 2 },
20 { .id = UDP_LEN, .len = 2, .offset = 4 },
21 { .id = UDP_CSUM, .len = 2, .offset = 6 },
24 static void udp_header_init(struct proto_hdr *hdr)
26 proto_lower_default_add(hdr, PROTO_IP4);
28 proto_header_fields_add(hdr, udp_fields, array_size(udp_fields));
31 static void udp_field_changed(struct proto_field *field)
33 field->hdr->is_csum_valid = false;
36 static void udp_csum_update(struct proto_hdr *hdr)
38 struct proto_hdr *lower;
39 uint16_t total_len;
40 uint16_t csum;
42 if (hdr->is_csum_valid)
43 return;
44 if (proto_hdr_field_is_set(hdr, UDP_CSUM))
45 return;
46 lower = proto_lower_header(hdr);
47 if (!lower)
48 return;
50 total_len = packet_get(hdr->pkt_id)->len - hdr->pkt_offset;
52 proto_hdr_field_set_default_be16(hdr, UDP_CSUM, 0);
54 switch (lower->ops->id) {
55 case PROTO_IP4:
56 csum = p4_csum((void *) proto_header_ptr(lower), proto_header_ptr(hdr),
57 total_len, IPPROTO_UDP);
58 break;
59 case PROTO_IP6:
60 csum = p6_csum((void *) proto_header_ptr(lower), proto_header_ptr(hdr),
61 total_len, IPPROTO_UDP);
62 break;
63 default:
64 csum = 0;
65 break;
68 proto_hdr_field_set_default_be16(hdr, UDP_CSUM, bswap_16(csum));
69 hdr->is_csum_valid = true;
72 static void udp_packet_finish(struct proto_hdr *hdr)
74 struct packet *pkt = current_packet();
75 uint16_t total_len;
77 total_len = pkt->len - hdr->pkt_offset;
78 proto_hdr_field_set_default_be16(hdr, UDP_LEN, total_len);
80 udp_csum_update(hdr);
83 static void udp_set_next_proto(struct proto_hdr *hdr, enum proto_id pid)
85 uint16_t dport;
87 switch (pid) {
88 case PROTO_DNS:
89 dport = 53;
90 break;
91 default:
92 bug();
95 proto_hdr_field_set_default_be16(hdr, UDP_DPORT, dport);
98 static const struct proto_ops udp_proto_ops = {
99 .id = PROTO_UDP,
100 .layer = PROTO_L4,
101 .header_init = udp_header_init,
102 .packet_update = udp_csum_update,
103 .packet_finish = udp_packet_finish,
104 .field_changed = udp_field_changed,
105 .set_next_proto = udp_set_next_proto,
108 static struct proto_field tcp_fields[] = {
109 { .id = TCP_SPORT, .len = 2, .offset = 0 },
110 { .id = TCP_DPORT, .len = 2, .offset = 2 },
111 { .id = TCP_SEQ, .len = 4, .offset = 4 },
112 { .id = TCP_ACK_SEQ, .len = 4, .offset = 8 },
113 { .id = TCP_DOFF, .len = 2, .offset = 12, .shift = 12, .mask = 0xf000 },
114 /* reserved (4 bits) */
115 { .id = TCP_CWR, .len = 2, .offset = 12, .shift = 7, .mask = 0x0080 },
116 { .id = TCP_ECE, .len = 2, .offset = 12, .shift = 6, .mask = 0x0040 },
117 { .id = TCP_URG, .len = 2, .offset = 12, .shift = 5, .mask = 0x0020 },
118 { .id = TCP_ACK, .len = 2, .offset = 12, .shift = 4, .mask = 0x0010 },
119 { .id = TCP_PSH, .len = 2, .offset = 12, .shift = 3, .mask = 0x0008 },
120 { .id = TCP_RST, .len = 2, .offset = 12, .shift = 2, .mask = 0x0004 },
121 { .id = TCP_SYN, .len = 2, .offset = 12, .shift = 1, .mask = 0x0002 },
122 { .id = TCP_FIN, .len = 2, .offset = 12, .shift = 0, .mask = 0x0001 },
123 { .id = TCP_WINDOW, .len = 2, .offset = 14 },
124 { .id = TCP_CSUM, .len = 2, .offset = 16 },
125 { .id = TCP_URG_PTR, .len = 2, .offset = 18 },
128 static void tcp_header_init(struct proto_hdr *hdr)
130 proto_lower_default_add(hdr, PROTO_IP4);
132 proto_header_fields_add(hdr, tcp_fields, array_size(tcp_fields));
134 proto_hdr_field_set_default_be16(hdr, TCP_DOFF, 5);
137 static void tcp_field_changed(struct proto_field *field)
139 field->hdr->is_csum_valid = false;
142 static void tcp_csum_update(struct proto_hdr *hdr)
144 struct proto_hdr *lower = proto_lower_header(hdr);
145 struct packet *pkt = current_packet();
146 uint16_t total_len;
147 uint16_t csum;
149 if (hdr->is_csum_valid)
150 return;
151 if (proto_hdr_field_is_set(hdr, TCP_CSUM))
152 return;
154 if (!lower)
155 return;
157 total_len = pkt->len - hdr->pkt_offset;
159 proto_hdr_field_set_default_be16(hdr, TCP_CSUM, 0);
161 switch (lower->ops->id) {
162 case PROTO_IP4:
163 csum = p4_csum((void *) proto_header_ptr(lower), proto_header_ptr(hdr),
164 total_len, IPPROTO_TCP);
165 break;
166 case PROTO_IP6:
167 csum = p6_csum((void *) proto_header_ptr(lower), proto_header_ptr(hdr),
168 total_len, IPPROTO_TCP);
169 break;
170 default:
171 csum = 0;
172 break;
175 proto_hdr_field_set_default_be16(hdr, TCP_CSUM, bswap_16(csum));
176 hdr->is_csum_valid = true;
179 static void tcp_set_next_proto(struct proto_hdr *hdr, enum proto_id pid)
181 uint16_t dport;
183 switch (pid) {
184 case PROTO_DNS:
185 dport = 53;
186 break;
187 default:
188 bug();
191 proto_hdr_field_set_default_be16(hdr, TCP_DPORT, dport);
194 static const struct proto_ops tcp_proto_ops = {
195 .id = PROTO_TCP,
196 .layer = PROTO_L4,
197 .header_init = tcp_header_init,
198 .packet_update = tcp_csum_update,
199 .packet_finish = tcp_csum_update,
200 .field_changed = tcp_field_changed,
201 .set_next_proto = tcp_set_next_proto,
204 static struct proto_field icmpv4_fields[] = {
205 { .id = ICMPV4_TYPE, .len = 1, .offset = 0 },
206 { .id = ICMPV4_CODE, .len = 1, .offset = 1 },
207 { .id = ICMPV4_CSUM, .len = 2, .offset = 2 },
208 /* Echo/Ping fields */
209 { .id = ICMPV4_ID, .len = 2, .offset = 4 },
210 { .id = ICMPV4_SEQ, .len = 2, .offset = 6 },
211 /* Redirect field */
212 { .id = ICMPV4_REDIR_ADDR, .len = 4, .offset = 4 },
213 /* Next-hop MTU */
214 { .id = ICMPV4_MTU, .len = 2, .offset = 6 },
217 static void icmpv4_header_init(struct proto_hdr *hdr)
219 proto_lower_default_add(hdr, PROTO_IP4);
221 proto_header_fields_add(hdr, icmpv4_fields, array_size(icmpv4_fields));
224 static void icmpv4_csum_update(struct proto_hdr *hdr)
226 struct packet *pkt;
227 uint16_t csum;
229 if (hdr->is_csum_valid)
230 return;
231 if (proto_hdr_field_is_set(hdr, ICMPV4_CSUM))
232 return;
234 pkt = packet_get(hdr->pkt_id);
236 proto_hdr_field_set_default_u16(hdr, ICMPV4_CSUM, 0);
237 csum = htons(calc_csum(proto_header_ptr(hdr), pkt->len - hdr->pkt_offset));
238 proto_hdr_field_set_default_u16(hdr, ICMPV4_CSUM, bswap_16(csum));
240 hdr->is_csum_valid = true;
243 static void icmpv4_field_changed(struct proto_field *field)
245 field->hdr->is_csum_valid = false;
248 static const struct proto_ops icmpv4_proto_ops = {
249 .id = PROTO_ICMP4,
250 .layer = PROTO_L4,
251 .header_init = icmpv4_header_init,
252 .packet_update = icmpv4_csum_update,
253 .packet_finish = icmpv4_csum_update,
254 .field_changed = icmpv4_field_changed,
257 static struct proto_field icmpv6_fields[] = {
258 { .id = ICMPV6_TYPE, .len = 1, .offset = 0 },
259 { .id = ICMPV6_CODE, .len = 1, .offset = 1 },
260 { .id = ICMPV6_CSUM, .len = 2, .offset = 2 }
263 static void icmpv6_header_init(struct proto_hdr *hdr)
265 proto_lower_default_add(hdr, PROTO_IP6);
267 proto_header_fields_add(hdr, icmpv6_fields, array_size(icmpv6_fields));
270 static void icmpv6_csum_update(struct proto_hdr *hdr)
272 struct proto_hdr *lower = proto_lower_header(hdr);
273 struct packet *pkt = packet_get(hdr->pkt_id);
274 uint16_t total_len;
275 uint16_t csum;
277 if (unlikely(!lower))
278 return;
279 if (hdr->is_csum_valid)
280 return;
281 if (proto_hdr_field_is_set(hdr, ICMPV6_CSUM))
282 return;
284 total_len = pkt->len - hdr->pkt_offset;
286 proto_hdr_field_set_be16(hdr, ICMPV6_CSUM, 0);
288 if (likely(lower->ops->id == PROTO_IP6)) {
289 csum = p6_csum((void *) proto_header_ptr(lower), proto_header_ptr(hdr),
290 total_len, IPPROTO_ICMPV6);
292 proto_hdr_field_set_be16(hdr, ICMPV6_CSUM, bswap_16(csum));
293 hdr->is_csum_valid = true;
297 static void icmpv6_field_changed(struct proto_field *field)
299 field->hdr->is_csum_valid = false;
302 static struct proto_ops icmpv6_proto_ops = {
303 .id = PROTO_ICMP6,
304 .layer = PROTO_L4,
305 .header_init = icmpv6_header_init,
306 .packet_finish = icmpv6_csum_update,
307 .packet_update = icmpv6_csum_update,
308 .field_changed = icmpv6_field_changed,
311 void protos_l4_init(void)
313 proto_ops_register(&udp_proto_ops);
314 proto_ops_register(&tcp_proto_ops);
315 proto_ops_register(&icmpv4_proto_ops);
316 proto_ops_register(&icmpv6_proto_ops);