Home | History | Annotate | Download | only in tc
      1 /*
      2  * em_u32.c		U32 Ematch
      3  *
      4  *		This program is free software; you can distribute it and/or
      5  *		modify it under the terms of the GNU General Public License
      6  *		as published by the Free Software Foundation; either version
      7  *		2 of the License, or (at your option) any later version.
      8  *
      9  * Authors:	Thomas Graf <tgraf (at) suug.ch>
     10  */
     11 
     12 #include <stdio.h>
     13 #include <stdlib.h>
     14 #include <unistd.h>
     15 #include <syslog.h>
     16 #include <fcntl.h>
     17 #include <sys/socket.h>
     18 #include <netinet/in.h>
     19 #include <arpa/inet.h>
     20 #include <string.h>
     21 #include <errno.h>
     22 
     23 #include "m_ematch.h"
     24 
     25 extern struct ematch_util u32_ematch_util;
     26 
     27 static void u32_print_usage(FILE *fd)
     28 {
     29 	fprintf(fd,
     30 	    "Usage: u32(ALIGN VALUE MASK at [ nexthdr+ ] OFFSET)\n" \
     31 	    "where: ALIGN  := { u8 | u16 | u32 }\n" \
     32 	    "\n" \
     33 	    "Example: u32(u16 0x1122 0xffff at nexthdr+4)\n");
     34 }
     35 
     36 static int u32_parse_eopt(struct nlmsghdr *n, struct tcf_ematch_hdr *hdr,
     37 			  struct bstr *args)
     38 {
     39 	struct bstr *a;
     40 	int align, nh_len;
     41 	unsigned long key, mask, offmask = 0, offset;
     42 	struct tc_u32_key u_key;
     43 
     44 	memset(&u_key, 0, sizeof(u_key));
     45 
     46 #define PARSE_ERR(CARG, FMT, ARGS...) \
     47 	em_parse_error(EINVAL, args, CARG, &u32_ematch_util, FMT ,##ARGS)
     48 
     49 	if (args == NULL)
     50 		return PARSE_ERR(args, "u32: missing arguments");
     51 
     52 	if (!bstrcmp(args, "u8"))
     53 		align = 1;
     54 	else if (!bstrcmp(args, "u16"))
     55 		align = 2;
     56 	else if (!bstrcmp(args, "u32"))
     57 		align = 4;
     58 	else
     59 		return PARSE_ERR(args, "u32: invalid alignment");
     60 
     61 	a = bstr_next(args);
     62 	if (a == NULL)
     63 		return PARSE_ERR(a, "u32: missing key");
     64 
     65 	key = bstrtoul(a);
     66 	if (key == ULONG_MAX)
     67 		return PARSE_ERR(a, "u32: invalid key, must be numeric");
     68 
     69 	a = bstr_next(a);
     70 	if (a == NULL)
     71 		return PARSE_ERR(a, "u32: missing mask");
     72 
     73 	mask = bstrtoul(a);
     74 	if (mask == ULONG_MAX)
     75 		return PARSE_ERR(a, "u32: invalid mask, must be numeric");
     76 
     77 	a = bstr_next(a);
     78 	if (a == NULL || bstrcmp(a, "at") != 0)
     79 		return PARSE_ERR(a, "u32: missing \"at\"");
     80 
     81 	a = bstr_next(a);
     82 	if (a == NULL)
     83 		return PARSE_ERR(a, "u32: missing offset");
     84 
     85 	nh_len = strlen("nexthdr+");
     86 	if (a->len > nh_len && !memcmp(a->data, "nexthdr+", nh_len)) {
     87 		char buf[a->len - nh_len + 1];
     88 		offmask = -1;
     89 		memcpy(buf, a->data + nh_len, a->len - nh_len);
     90 		offset = strtoul(buf, NULL, 0);
     91 	} else if (!bstrcmp(a, "nexthdr+")) {
     92 		a = bstr_next(a);
     93 		if (a == NULL)
     94 			return PARSE_ERR(a, "u32: missing offset");
     95 		offset = bstrtoul(a);
     96 	} else
     97 		offset = bstrtoul(a);
     98 
     99 	if (offset == ULONG_MAX)
    100 		return PARSE_ERR(a, "u32: invalid offset");
    101 
    102 	if (a->next)
    103 		return PARSE_ERR(a->next, "u32: unexpected trailer");
    104 
    105 	switch (align) {
    106 		case 1:
    107 			if (key > 0xFF)
    108 				return PARSE_ERR(a, "Illegal key (>0xFF)");
    109 			if (mask > 0xFF)
    110 				return PARSE_ERR(a, "Illegal mask (>0xFF)");
    111 
    112 			key <<= 24 - ((offset & 3) * 8);
    113 			mask <<= 24 - ((offset & 3) * 8);
    114 			offset &= ~3;
    115 			break;
    116 
    117 		case 2:
    118 			if (key > 0xFFFF)
    119 				return PARSE_ERR(a, "Illegal key (>0xFFFF)");
    120 			if (mask > 0xFFFF)
    121 				return PARSE_ERR(a, "Illegal mask (>0xFFFF)");
    122 
    123 			if ((offset & 3) == 0) {
    124 				key <<= 16;
    125 				mask <<= 16;
    126 			}
    127 			offset &= ~3;
    128 			break;
    129 	}
    130 
    131 	key = htonl(key);
    132 	mask = htonl(mask);
    133 
    134 	if (offset % 4)
    135 		return PARSE_ERR(a, "u32: invalid offset alignment, " \
    136 		    "must be aligned to 4.");
    137 
    138 	key &= mask;
    139 
    140 	u_key.mask = mask;
    141 	u_key.val = key;
    142 	u_key.off = offset;
    143 	u_key.offmask = offmask;
    144 
    145 	addraw_l(n, MAX_MSG, hdr, sizeof(*hdr));
    146 	addraw_l(n, MAX_MSG, &u_key, sizeof(u_key));
    147 
    148 #undef PARSE_ERR
    149 	return 0;
    150 }
    151 
    152 static int u32_print_eopt(FILE *fd, struct tcf_ematch_hdr *hdr, void *data,
    153 			  int data_len)
    154 {
    155 	struct tc_u32_key *u_key = data;
    156 
    157 	if (data_len < sizeof(*u_key)) {
    158 		fprintf(stderr, "U32 header size mismatch\n");
    159 		return -1;
    160 	}
    161 
    162 	fprintf(fd, "%08x/%08x at %s%d",
    163 	    (unsigned int) ntohl(u_key->val),
    164 	    (unsigned int) ntohl(u_key->mask),
    165 	    u_key->offmask ? "nexthdr+" : "",
    166 	    u_key->off);
    167 
    168 	return 0;
    169 }
    170 
    171 struct ematch_util u32_ematch_util = {
    172 	.kind = "u32",
    173 	.kind_num = TCF_EM_U32,
    174 	.parse_eopt = u32_parse_eopt,
    175 	.print_eopt = u32_print_eopt,
    176 	.print_usage = u32_print_usage
    177 };
    178