1/**
2 * \file
3 * \brief Helper functoin and additional tools used by libbfdmux
4*/
5/*
6 * Copyright (c) 2009, 2010, ETH Zurich.
7 * All rights reserved.
8 *
9 * This file is distributed under the terms in the attached LICENSE file.
10 * If you do not find this file, copies can be found by writing to:
11 * ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group.
12 */
13
14#ifndef DOXYGEN
15// exclude system headers from documentation
16
17//#include <errno.h>
18#include <stdlib.h>
19#include <string.h>
20
21#endif							// DOXYGEN
22
23#include <barrelfish/barrelfish.h>
24
25#include <bfdmuxtools/tools.h>
26
27/**
28 * \brief Finds the index of the most significant 1-bit in 'value'
29 * @param value The integer to be analyzed
30 * @return The index of the most significant 1-bit in value (bits numbered 1..64); 0 if value = 0.
31 */
32
33//inline int find_msb(uint64_t value);
34
35inline int
36find_msb(uint64_t value)
37{
38	int             msb;
39	uint32_t        lo,
40	                hi;
41
42	msb = 0;
43	lo = (uint32_t) value;
44	hi = (uint32_t) (value >> 32);
45
46	// Perform binary search for most significant bit
47	if (hi) {
48		msb |= 0x20;
49		lo = hi & 0xFFFF;
50		hi = hi >> 16;
51	} else {
52		hi = lo >> 16;
53		lo = lo & 0xFFFF;
54	}
55	if (hi) {
56		msb |= 0x10;
57		lo = hi & 0x00FF;
58		hi = hi >> 8;
59	} else {
60		hi = lo >> 8;
61		lo = lo & 0x00FF;
62	}
63	if (hi) {
64		msb |= 0x08;
65		lo = hi & 0x000F;
66		hi = hi >> 4;
67	} else {
68		hi = lo >> 4;
69		lo = lo & 0x000F;
70	}
71	if (hi) {
72		msb |= 0x04;
73		lo = hi & 0x0003;
74		hi = hi >> 2;
75	} else {
76		hi = lo >> 2;
77		lo = lo & 0x0003;
78	}
79	if (hi) {
80		msb |= 0x02;
81		lo = hi & 0x0001;
82		hi = hi >> 1;
83	} else {
84		hi = lo >> 1;
85		lo = lo & 0x0001;
86	}
87	if (hi) {
88		msb |= 0x01;
89	}
90
91	return msb + (value >> msb);	// Modifies bit numbering from 0..63
92	// to range from 1..64
93}
94
95/**
96 * \brief Returns a string with pos-1 spaces and a '^' character. Used to indicate error position in filter string!
97 * @param pos The position to point at
98 * @return A string with a '^' character at the given position. Caller should free memory after use!
99 */
100char           *
101get_error_position_string(int pos)
102{
103	int             i = 0;
104	char           *res = malloc(pos + 1);
105	while ((i + 1) < pos) {
106		res[i] = ' ';
107		i++;
108	}
109	res[i] = '^';
110	res[i + 1] = 0;
111	return res;
112}
113
114/**
115 * \brief Parses a string consisting of hex digits to a byte array
116 * @param str The string to be parsed, e.g. "fe01abc9"
117 * @return A byte array, e.g. 0xfe 0x01 0xab 0xc9. Caller should free the array after use!
118
119uint8_t        *
120parse_hex_input(char *str)
121{
122	char            hex[3];
123	int             i;
124	int             len = strlen(str);
125	if ((len % 2) != 0) {
126		return 0;
127	}
128
129	len = len / 2;
130	char           *res = malloc(len);
131	hex[2] = 0;
132	for (i = 0; i < len; i++) {
133		hex[0] = str[i * 2];
134		hex[1] = str[i * 2 + 1];
135		errno = 0;
136		char           *endptr = (char *) &hex;
137		uint8_t         val = strtol((char *) &hex, &endptr, 16);
138		if ((errno == 0) && (endptr != (char *) &hex)) {
139			res[i] = val;
140		} else {
141			free(res);
142			return 0;
143		}
144	}
145	return (uint8_t *) res;
146}
147*/
148
149/*
150 * Filter builder
151 */
152
153#define MAC_ADDR_SIZE 6
154
155static inline bool if_any_mac(struct eth_addr addr){
156
157    int i;
158    for(i = 0; i < MAC_ADDR_SIZE; i++) {
159	if(addr.addr[i] != 0xff) {
160	    return false;
161	}
162    }
163    return true;
164}
165
166/*
167 * SrcMac field: size 48 bit offset = 0
168 * DstMac field: size 48 bit offset = 48
169 */
170/*
171 * This creates a Mac filter with a dst address
172 */
173
174char* build_dst_mac_filter(struct eth_addr dst)
175{
176    uint32_t first_four = 0;
177    uint16_t last_two = 0;
178    size_t max_len = 64;
179    char *filter = malloc(max_len);
180    filter[0] = 0x0;
181
182    first_four = (uint32_t) dst.addr[3];
183    first_four |= ((uint32_t) dst.addr[2]) << 8;
184    first_four |= ((uint32_t) dst.addr[1]) << 16;
185    first_four |= ((uint32_t) dst.addr[0]) << 24;
186    last_two = (uint16_t) dst.addr[5];
187    last_two |= ((uint16_t) dst.addr[4]) << 8;
188
189    if(!if_any_mac(dst)) {
190	snprintf(filter, max_len, "int32[0]==%"PRIu32"&&int16[4]==%"PRIu16,
191	        first_four, last_two);
192    }
193    if (strlen(filter) == 0) {
194	snprintf(filter, max_len, "1");
195    }
196    return filter;
197
198}
199
200char* build_src_mac_filter(struct eth_addr src)
201{
202    uint32_t first_four = 0;
203    uint16_t last_two = 0;
204    size_t max_len = 64;
205    char *filter = malloc(max_len);
206    filter[0] = 0x0;
207
208    first_four = (uint32_t) src.addr[3];
209    first_four |= ((uint32_t) src.addr[2]) << 8;
210    first_four |= ((uint32_t) src.addr[1]) << 16;
211    first_four |= ((uint32_t) src.addr[0]) << 24;
212    last_two = (uint16_t) src.addr[5];
213    last_two |= ((uint16_t) src.addr[4]) << 8;
214
215    if(!if_any_mac(src)) {
216	snprintf(filter, max_len, "int32[6]==%"PRIu32"&&int16[10]==%"PRIu16,
217	        first_four, last_two);
218    }
219    if (strlen(filter) == 0) {
220	snprintf(filter, max_len, "1");
221    }
222    return filter;
223
224}
225
226/*
227 * SrcIP filed: size=32bit, offset=12Bytes
228 * DstIP field: size=32bit, offset=16Bytes
229 */
230/**
231 * \brief IPv4 filter template
232 *
233 * Create an IPv4 filter based on a source IP and a destination IP. The source IP is a 32bit field in the
234 * IPv4 header starting at offset 12Bytes, the destination IP is also a 32bit field starting at 16Bytes.
235 * @param srcip Filter packets coming from this source IP (BFDMUX_IP_ADDR_ANY for any source)
236 * @param dstip Filter packets going to this destination IP (BFDMUX_IP_ADDR_ANY for any target)
237 * @return A filter string. Caller has to free it after use.
238 */
239/* FIXME: It is assumed that if it is non-ARP packet then it should be IP packet.
240 * So no check in Ethernet packet header to see if it is IP or not. PS */
241char           *
242build_ipv4_filter(addr_t srcip, addr_t dstip)
243{
244	size_t          max_len = 64;
245	char           *filter = malloc(max_len);
246	filter[0] = 0x0;			// strlen(filter) = 0;
247	if (srcip != BFDMUX_IP_ADDR_ANY)
248		snprintf(filter, max_len, "int32[26]==%"PRIu32"", (uint32_t) srcip);
249	if (dstip != BFDMUX_IP_ADDR_ANY) {
250		if (srcip != BFDMUX_IP_ADDR_ANY)
251			snprintf(filter + strlen(filter), max_len, "&&");
252		snprintf(filter + strlen(filter), max_len, "int32[30]==%"PRIu32"",
253				 (uint32_t) dstip);
254	}
255
256	if (strlen(filter) == 0) {	// srcip == BFDMUX_IP_ADDR_ANY && dstip ==
257		// BFDMUX_IP_ADDR_ANY
258		snprintf(filter, max_len, "1");
259	}
260	return filter;
261}
262
263/*
264 * Protocol field: size=8bit,  offset=9Bytes (in IP Header)
265 * ICMP Protocol number: 0x01
266 */
267/**
268 * \brief ICMP filter template
269 *
270 * Create a generic ICMP packet filter
271 * @return A filter sting. Caller has to free it after use.
272 */
273char *
274build_icmp_filter(void)
275{
276	size_t          max_len = 128;
277	char           *filter = malloc(max_len);
278	snprintf(filter, max_len, "int8[23]==%u", 0x01);
279
280	return filter;
281}
282
283/**
284 * \brief ICMP over IPv4 filter template
285 *
286 * This function build a ICMP over IPv4 filter based on the given arguments
287 * using the build_tcp_filter and build_ipv4_filter helper functions.
288 * @param srcip Source IP-Address to filter for (BFDMUX_IP_ADDR_ANY for any)
289 * @param dstip Destination IP-Address to filter for (BFDMUX_IP_ADDR_ANY for any)
290 * @return A filter string. Caller has to free it after use.
291 */
292char *
293build_ipv4_icmp_filter(addr_t srcip, addr_t dstip)
294{
295	char *ip_filter = build_ipv4_filter(srcip, dstip);
296	char *icmp_filter = build_icmp_filter();
297	int filter_len = strlen(ip_filter) + strlen(icmp_filter) + 2 + 1;
298	/* 2: &&, 1: zero byte */
299
300	char *filter = malloc(filter_len);
301	snprintf(filter, filter_len, "%s&&%s", ip_filter, icmp_filter);
302	free(ip_filter);
303	free(icmp_filter);
304	return filter;
305}
306
307
308
309
310/*
311 * Protocol field: size=8bit,  offset=9Bytes (in IP Header)
312 * SrcPort field:  size=16bit, offset=20Bytes (with IP Header)
313 * DstPort field:  size=16bit, offset=22Bytes (with IP Header)
314 * TCP Protocol number: 0x06
315 */
316/**
317 * \brief TCP filter template
318 *
319 * Create a TCP filter based on the source and destination TCP Port. This filter looks for the TCP protocol number (0x06)
320 * in the IP header and sets the 16bit long source port field positioned at offset 20Bytes (with IP header) and the 16bit long
321 * destination port filed positioned at offset 22Bytes to the given arguments.
322 * @param srcport TCP source port to filter on (PORT_ANY for any port)
323 * @param dstport TCP destination port to filter on (PORT_ANY for any port)
324 * @return A filter sting. Caller has to free it after use.
325 */
326char           *
327build_tcp_filter(port_t srcport, port_t dstport)
328{
329	size_t          max_len = 128;
330	char           *filter = malloc(max_len);
331	snprintf(filter, max_len, "int8[23]==%u", 0x06);
332
333	if (srcport != PORT_ANY)
334		snprintf(filter + strlen(filter), max_len, "&&int16[34]==%u",
335				 (uint16_t) srcport);
336	if (dstport != PORT_ANY) {
337		snprintf(filter + strlen(filter), max_len, "&&int16[36]==%u",
338				 (uint16_t) dstport);
339	}
340
341	return filter;
342}
343
344/*
345 * Protocol field: size=8bit,  offset=9Bytes (in IP Header)
346 * SrcPort field:  size=16bit, offset=20Bytes (with IP Header)
347 * DstPort field:  size=16bit, offset=22Bytes (with IP Header)
348 * UDP Protocol number: 0x11
349 */
350/**
351 * \brief UDP filter template
352 *
353 * Create a UDP filter based on the source and destination UDP Port. This filter looks for the UDP protocol number (0x11)
354 * in the IP header and sets the 16bit long source port field positioned at offset 20Bytes (with IP header) and the 16bit long
355 * destination port filed positioned at offset 22Bytes to the given arguments.
356 * @param srcport UDP source port to filter on (PORT_ANY for any port)
357 * @param dstport UDP destination port to filter on (PORT_ANY for any port)
358 * @return A filter sting. Caller has to free it after use.
359 */
360char           *
361build_udp_filter(port_t srcport, port_t dstport)
362{
363	size_t          max_len = 128;
364	char           *filter = malloc(max_len);
365	snprintf(filter, max_len, "int8[23]==%u", 0x11);
366
367	if (srcport != PORT_ANY)
368		snprintf(filter + strlen(filter), max_len, "&&int16[34]==%u",
369				 (uint16_t) srcport);
370	if (dstport != PORT_ANY) {
371		snprintf(filter + strlen(filter), max_len, "&&int16[36]==%u",
372				 (uint16_t) dstport);
373	}
374
375	return filter;
376}
377
378/**
379 * \brief TCP over IPv4 filter template
380 *
381 * This function build a TCP over IPv4 filter based on the given arguments using the build_tcp_filter and build_ipv4_filter
382 * helper functions.
383 * @param srcip Source IP-Address to filter for (BFDMUX_IP_ADDR_ANY for any)
384 * @param dstip Destination IP-Address to filter for (BFDMUX_IP_ADDR_ANY for any)
385 * @param srcport Source TCP port to filter for (PORT_ANY for any)
386 * @param dstport Destination TCP port to filter for (PORT_ANY for any)
387 * @return A filter string. Caller has to free it after use.
388 */
389char           *
390build_ipv4_tcp_filter(addr_t srcip, addr_t dstip, port_t srcport,
391					  port_t dstport)
392{
393	char           *ip_filter = build_ipv4_filter(srcip, dstip);
394	char           *tcp_filter = build_tcp_filter(srcport, dstport);
395	int filter_len = strlen(ip_filter) + strlen(tcp_filter) + 2 + 1;	// 2:
396																					//
397	//
398	// &&,
399	// 1:
400	// zero
401	// byte
402	char           *filter = malloc(filter_len);
403	snprintf(filter, filter_len, "%s&&%s", ip_filter, tcp_filter);
404	free(ip_filter);
405	free(tcp_filter);
406	return filter;
407}
408
409/**
410 * \brief UDP over IPv4 filter template
411 *
412 * This function build a UDP over IPv4 filter based on the given arguments using the build_tcp_filter and build_ipv4_filter
413 * helper functions.
414 * @param srcip Source IP-Address to filter for (BFDMUX_IP_ADDR_ANY for any)
415 * @param dstip Destination IP-Address to filter for (BFDMUX_IP_ADDR_ANY for any)
416 * @param srcport Source UDP port to filter for (PORT_ANY for any)
417 * @param dstport Destination UDP port to filter for (PORT_ANY for any)
418 * @return A filter string. Caller has to free it after use.
419 */
420char           *
421build_ipv4_udp_filter(addr_t srcip, addr_t dstip, port_t srcport,
422					  port_t dstport)
423{
424	char           *ip_filter = build_ipv4_filter(srcip, dstip);
425	char           *udp_filter = build_udp_filter(srcport, dstport);
426	size_t          filter_len = strlen(ip_filter) + strlen(udp_filter) + 2 + 1;	// 2:
427																					//
428	//
429	// &&,
430	// 1:
431	// zero
432	// byte
433	char           *filter = malloc(filter_len);
434	snprintf(filter, filter_len, "%s&&%s", ip_filter, udp_filter);
435	free(ip_filter);
436	free(udp_filter);
437	return filter;
438}
439
440char* build_ether_dst_ipv4_udp_filter(struct eth_addr dst, addr_t srcip,
441	addr_t dstip, port_t srcport, port_t dstport)
442{
443    char *mac_filter = build_dst_mac_filter(dst);
444    char *ip_filter = build_ipv4_filter(srcip, dstip);
445    char *udp_filter = build_udp_filter(srcport, dstport);
446    size_t filter_len = strlen(mac_filter) + strlen(ip_filter) +
447	strlen(udp_filter) + 4 + 1;
448    // 4: 2 * &&, 1: zero byte
449    char *filter = malloc(filter_len);
450    snprintf(filter, filter_len, "%s&&%s&&%s", mac_filter, ip_filter,
451	    udp_filter);
452    free(mac_filter);
453    free(ip_filter);
454    free(udp_filter);
455    return filter;
456
457}
458
459char* build_ether_dst_ipv4_tcp_filter(struct eth_addr dst, addr_t srcip,
460	addr_t dstip, port_t srcport, port_t dstport)
461{
462    char *mac_filter = build_dst_mac_filter(dst);
463    char *ip_filter = build_ipv4_filter(srcip, dstip);
464    char *tcp_filter = build_tcp_filter(srcport, dstport);
465    size_t filter_len = strlen(mac_filter) + strlen(ip_filter) +
466	strlen(tcp_filter) + 4 + 1;
467    // 4: 2 * &&, 1: zero byte
468    char *filter = malloc(filter_len);
469    snprintf(filter, filter_len, "%s&&%s&&%s", mac_filter, ip_filter,
470	    tcp_filter);
471    free(mac_filter);
472    free(ip_filter);
473    free(tcp_filter);
474    return filter;
475}
476
477char* build_ether_src_ipv4_udp_filter(struct eth_addr src, addr_t srcip,
478	addr_t dstip, port_t srcport, port_t dstport)
479{
480    char *mac_filter = build_src_mac_filter(src);
481    char *ip_filter = build_ipv4_filter(srcip, dstip);
482    char *udp_filter = build_udp_filter(srcport, dstport);
483    size_t filter_len = strlen(mac_filter) +
484	strlen(ip_filter) + strlen(udp_filter) + 4 + 1;
485    // 4: 2 * &&, 1: zero byte
486    char *filter = malloc(filter_len);
487    snprintf(filter, filter_len, "%s&&%s&&%s", mac_filter, ip_filter,
488	    udp_filter);
489    free(mac_filter);
490    free(ip_filter);
491    free(udp_filter);
492    return filter;
493
494}
495
496char* build_ether_src_ipv4_tcp_filter(struct eth_addr src, addr_t srcip,
497	addr_t dstip, port_t srcport, port_t dstport)
498{
499    char *mac_filter = build_src_mac_filter(src);
500    char *ip_filter = build_ipv4_filter(srcip, dstip);
501    char *tcp_filter = build_tcp_filter(srcport, dstport);
502    size_t filter_len = strlen(mac_filter) +
503	strlen(ip_filter) + strlen(tcp_filter) + 4 + 1;
504    // 4: 2 * &&, 1: zero byte
505    char *filter = malloc(filter_len);
506    snprintf(filter, filter_len, "%s&&%s&&%s", mac_filter, ip_filter,
507	    tcp_filter);
508    free(mac_filter);
509    free(ip_filter);
510    free(tcp_filter);
511    return filter;
512}
513
514char* build_generic_arp_reply_filter(void)
515{
516    size_t max_len = 128;
517    char *filter = malloc(max_len);
518    assert(filter);
519    memset(filter, 0, max_len);
520    snprintf(filter, max_len, "int16[12]==%u", 0x0806 /* ETHTYPE_ARP */);
521		/* FIXME: why following filter is broken?
522		 * Question is, what is the correct location of value 0x0002? */
523/*    snprintf(filter, max_len, "int16[12]==%u&&int16[20]==%u", (0x0806),
524    		(0x0002));
525*/
526
527		/* The hardcoded values for protocol field are already
528		 * in the network order. */
529    return filter;
530}
531
532char* build_arp_transmit_filter(struct eth_addr src)
533{
534    char *mac = build_src_mac_filter(src);
535    size_t max_len = 128;
536    char *filter = malloc(max_len);
537    assert(mac);
538    assert(filter);
539    memset(filter, 0, max_len);
540    snprintf(filter, max_len, "int16[12]==%u&&%s", 0x0806, mac);
541    /*FIXME: why these values are not htons()? */
542    return filter;
543}
544
545