trafgen: icmpv4: Update csum at runtime if needed
[netsniff-ng.git] / trafgen_l4.c
blob89bb2527f56d6248fe109c9ade21496763d515d4
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_packet_finish(struct proto_hdr *hdr)
33 struct proto_hdr *lower = proto_lower_header(hdr);
34 struct packet *pkt = current_packet();
35 uint16_t total_len;
36 uint16_t csum;
38 total_len = pkt->len - hdr->pkt_offset;
39 proto_field_set_default_be16(hdr, UDP_LEN, total_len);
41 if (proto_field_is_set(hdr, UDP_CSUM))
42 return;
44 if (!lower)
45 return;
47 switch (lower->ops->id) {
48 case PROTO_IP4:
49 csum = p4_csum((void *) proto_header_ptr(lower), proto_header_ptr(hdr),
50 total_len, IPPROTO_UDP);
51 break;
52 case PROTO_IP6:
53 csum = p6_csum((void *) proto_header_ptr(lower), proto_header_ptr(hdr),
54 total_len, IPPROTO_UDP);
55 break;
56 default:
57 csum = 0;
58 break;
61 proto_field_set_be16(hdr, UDP_CSUM, bswap_16(csum));
64 static const struct proto_ops udp_proto_ops = {
65 .id = PROTO_UDP,
66 .layer = PROTO_L4,
67 .header_init = udp_header_init,
68 .packet_finish = udp_packet_finish,
71 static struct proto_field tcp_fields[] = {
72 { .id = TCP_SPORT, .len = 2, .offset = 0 },
73 { .id = TCP_DPORT, .len = 2, .offset = 2 },
74 { .id = TCP_SEQ, .len = 4, .offset = 4 },
75 { .id = TCP_ACK_SEQ, .len = 4, .offset = 8 },
76 { .id = TCP_DOFF, .len = 2, .offset = 12, .shift = 12, .mask = 0xf000 },
77 /* reserved (4 bits) */
78 { .id = TCP_CWR, .len = 2, .offset = 12, .shift = 7, .mask = 0x0080 },
79 { .id = TCP_ECE, .len = 2, .offset = 12, .shift = 6, .mask = 0x0040 },
80 { .id = TCP_URG, .len = 2, .offset = 12, .shift = 5, .mask = 0x0020 },
81 { .id = TCP_ACK, .len = 2, .offset = 12, .shift = 4, .mask = 0x0010 },
82 { .id = TCP_PSH, .len = 2, .offset = 12, .shift = 3, .mask = 0x0008 },
83 { .id = TCP_RST, .len = 2, .offset = 12, .shift = 2, .mask = 0x0004 },
84 { .id = TCP_SYN, .len = 2, .offset = 12, .shift = 1, .mask = 0x0002 },
85 { .id = TCP_FIN, .len = 2, .offset = 12, .shift = 0, .mask = 0x0001 },
86 { .id = TCP_WINDOW, .len = 2, .offset = 14 },
87 { .id = TCP_CSUM, .len = 2, .offset = 16 },
88 { .id = TCP_URG_PTR, .len = 2, .offset = 18 },
91 static void tcp_header_init(struct proto_hdr *hdr)
93 proto_lower_default_add(hdr, PROTO_IP4);
95 proto_header_fields_add(hdr, tcp_fields, array_size(tcp_fields));
97 proto_field_set_default_be16(hdr, TCP_DOFF, 5);
100 static void tcp_packet_finish(struct proto_hdr *hdr)
102 struct proto_hdr *lower = proto_lower_header(hdr);
103 struct packet *pkt = current_packet();
104 uint16_t total_len;
105 uint16_t csum;
107 if (proto_field_is_set(hdr, TCP_CSUM))
108 return;
110 if (!lower)
111 return;
113 total_len = pkt->len - hdr->pkt_offset;
115 switch (lower->ops->id) {
116 case PROTO_IP4:
117 csum = p4_csum((void *) proto_header_ptr(lower), proto_header_ptr(hdr),
118 total_len, IPPROTO_TCP);
119 break;
120 case PROTO_IP6:
121 csum = p6_csum((void *) proto_header_ptr(lower), proto_header_ptr(hdr),
122 total_len, IPPROTO_TCP);
123 break;
124 default:
125 csum = 0;
126 break;
129 proto_field_set_be16(hdr, TCP_CSUM, bswap_16(csum));
132 static const struct proto_ops tcp_proto_ops = {
133 .id = PROTO_TCP,
134 .layer = PROTO_L4,
135 .header_init = tcp_header_init,
136 .packet_finish = tcp_packet_finish,
139 static struct proto_field icmpv4_fields[] = {
140 { .id = ICMPV4_TYPE, .len = 1, .offset = 0 },
141 { .id = ICMPV4_CODE, .len = 1, .offset = 1 },
142 { .id = ICMPV4_CSUM, .len = 2, .offset = 2 },
143 /* Echo/Ping fields */
144 { .id = ICMPV4_ID, .len = 2, .offset = 4 },
145 { .id = ICMPV4_SEQ, .len = 2, .offset = 6 },
146 /* Redirect field */
147 { .id = ICMPV4_REDIR_ADDR, .len = 4, .offset = 4 },
148 /* Next-hop MTU */
149 { .id = ICMPV4_MTU, .len = 2, .offset = 6 },
152 static void icmpv4_header_init(struct proto_hdr *hdr)
154 proto_lower_default_add(hdr, PROTO_IP4);
156 proto_header_fields_add(hdr, icmpv4_fields, array_size(icmpv4_fields));
159 static void icmpv4_csum_update(struct proto_hdr *hdr)
161 struct packet *pkt;
162 uint16_t csum;
164 if (hdr->is_csum_valid)
165 return;
166 if (proto_field_is_set(hdr, ICMPV4_CSUM))
167 return;
169 pkt = packet_get(hdr->pkt_id);
171 proto_field_set_default_u16(hdr, ICMPV4_CSUM, 0);
172 csum = htons(calc_csum(proto_header_ptr(hdr), pkt->len - hdr->pkt_offset));
173 proto_field_set_default_u16(hdr, ICMPV4_CSUM, bswap_16(csum));
175 hdr->is_csum_valid = true;
178 static void icmpv4_field_changed(struct proto_field *field)
180 field->hdr->is_csum_valid = false;
183 static const struct proto_ops icmpv4_proto_ops = {
184 .id = PROTO_ICMP4,
185 .layer = PROTO_L4,
186 .header_init = icmpv4_header_init,
187 .packet_update = icmpv4_csum_update,
188 .packet_finish = icmpv4_csum_update,
189 .field_changed = icmpv4_field_changed,
192 static struct proto_field icmpv6_fields[] = {
193 { .id = ICMPV6_TYPE, .len = 1, .offset = 0 },
194 { .id = ICMPV6_CODE, .len = 1, .offset = 1 },
195 { .id = ICMPV6_CSUM, .len = 2, .offset = 2 }
198 static void icmpv6_header_init(struct proto_hdr *hdr)
200 proto_lower_default_add(hdr, PROTO_IP6);
202 proto_header_fields_add(hdr, icmpv6_fields, array_size(icmpv6_fields));
205 static void icmpv6_packet_finish(struct proto_hdr *hdr)
207 struct proto_hdr *lower = proto_lower_header(hdr);
208 struct packet *pkt = current_packet();
209 uint16_t total_len;
210 uint16_t csum;
212 if (proto_field_is_set(hdr, ICMPV6_CSUM))
213 return;
215 if (!lower)
216 return;
218 total_len = pkt->len - hdr->pkt_offset;
220 switch (lower->ops->id) {
221 case PROTO_IP6:
222 csum = p6_csum((void *) proto_header_ptr(lower), proto_header_ptr(hdr),
223 total_len, IPPROTO_ICMPV6);
224 break;
225 default:
226 csum = 0;
227 break;
230 proto_field_set_be16(hdr, ICMPV6_CSUM, bswap_16(csum));
233 static struct proto_ops icmpv6_proto_ops = {
234 .id = PROTO_ICMP6,
235 .layer = PROTO_L4,
236 .header_init = icmpv6_header_init,
237 .packet_finish = icmpv6_packet_finish,
240 void protos_l4_init(void)
242 proto_ops_register(&udp_proto_ops);
243 proto_ops_register(&tcp_proto_ops);
244 proto_ops_register(&icmpv4_proto_ops);
245 proto_ops_register(&icmpv6_proto_ops);