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 #define PARSE_ERR(CARG, FMT, ARGS...) \
     45 	em_parse_error(EINVAL, args, CARG, &u32_ematch_util, FMT, ##ARGS)
     46 
     47 	if (args == NULL)
     48 		return PARSE_ERR(args, "u32: missing arguments");
     49 
     50 	if (!bstrcmp(args, "u8"))
     51 		align = 1;
     52 	else if (!bstrcmp(args, "u16"))
     53 		align = 2;
     54 	else if (!bstrcmp(args, "u32"))
     55 		align = 4;
     56 	else
     57 		return PARSE_ERR(args, "u32: invalid alignment");
     58 
     59 	a = bstr_next(args);
     60 	if (a == NULL)
     61 		return PARSE_ERR(a, "u32: missing key");
     62 
     63 	key = bstrtoul(a);
     64 	if (key == ULONG_MAX)
     65 		return PARSE_ERR(a, "u32: invalid key, must be numeric");
     66 
     67 	a = bstr_next(a);
     68 	if (a == NULL)
     69 		return PARSE_ERR(a, "u32: missing mask");
     70 
     71 	mask = bstrtoul(a);
     72 	if (mask == ULONG_MAX)
     73 		return PARSE_ERR(a, "u32: invalid mask, must be numeric");
     74 
     75 	a = bstr_next(a);
     76 	if (a == NULL || bstrcmp(a, "at") != 0)
     77 		return PARSE_ERR(a, "u32: missing \"at\"");
     78 
     79 	a = bstr_next(a);
     80 	if (a == NULL)
     81 		return PARSE_ERR(a, "u32: missing offset");
     82 
     83 	nh_len = strlen("nexthdr+");
     84 	if (a->len > nh_len && !memcmp(a->data, "nexthdr+", nh_len)) {
     85 		char buf[a->len - nh_len + 1];
     86 
     87 		offmask = -1;
     88 		memcpy(buf, a->data + nh_len, a->len - nh_len);
     89 		offset = strtoul(buf, NULL, 0);
     90 	} else if (!bstrcmp(a, "nexthdr+")) {
     91 		a = bstr_next(a);
     92 		if (a == NULL)
     93 			return PARSE_ERR(a, "u32: missing offset");
     94 		offset = bstrtoul(a);
     95 	} else
     96 		offset = bstrtoul(a);
     97 
     98 	if (offset == ULONG_MAX)
     99 		return PARSE_ERR(a, "u32: invalid offset");
    100 
    101 	if (a->next)
    102 		return PARSE_ERR(a->next, "u32: unexpected trailer");
    103 
    104 	switch (align) {
    105 		case 1:
    106 			if (key > 0xFF)
    107 				return PARSE_ERR(a, "Illegal key (>0xFF)");
    108 			if (mask > 0xFF)
    109 				return PARSE_ERR(a, "Illegal mask (>0xFF)");
    110 
    111 			key <<= 24 - ((offset & 3) * 8);
    112 			mask <<= 24 - ((offset & 3) * 8);
    113 			offset &= ~3;
    114 			break;
    115 
    116 		case 2:
    117 			if (key > 0xFFFF)
    118 				return PARSE_ERR(a, "Illegal key (>0xFFFF)");
    119 			if (mask > 0xFFFF)
    120 				return PARSE_ERR(a, "Illegal mask (>0xFFFF)");
    121 
    122 			if ((offset & 3) == 0) {
    123 				key <<= 16;
    124 				mask <<= 16;
    125 			}
    126 			offset &= ~3;
    127 			break;
    128 	}
    129 
    130 	key = htonl(key);
    131 	mask = htonl(mask);
    132 
    133 	if (offset % 4)
    134 		return PARSE_ERR(a, "u32: invalid offset alignment, " \
    135 		    "must be aligned to 4.");
    136 
    137 	key &= mask;
    138 
    139 	u_key.mask = mask;
    140 	u_key.val = key;
    141 	u_key.off = offset;
    142 	u_key.offmask = offmask;
    143 
    144 	addraw_l(n, MAX_MSG, hdr, sizeof(*hdr));
    145 	addraw_l(n, MAX_MSG, &u_key, sizeof(u_key));
    146 
    147 #undef PARSE_ERR
    148 	return 0;
    149 }
    150 
    151 static int u32_print_eopt(FILE *fd, struct tcf_ematch_hdr *hdr, void *data,
    152 			  int data_len)
    153 {
    154 	struct tc_u32_key *u_key = data;
    155 
    156 	if (data_len < sizeof(*u_key)) {
    157 		fprintf(stderr, "U32 header size mismatch\n");
    158 		return -1;
    159 	}
    160 
    161 	fprintf(fd, "%08x/%08x at %s%d",
    162 	    (unsigned int) ntohl(u_key->val),
    163 	    (unsigned int) ntohl(u_key->mask),
    164 	    u_key->offmask ? "nexthdr+" : "",
    165 	    u_key->off);
    166 
    167 	return 0;
    168 }
    169 
    170 struct ematch_util u32_ematch_util = {
    171 	.kind = "u32",
    172 	.kind_num = TCF_EM_U32,
    173 	.parse_eopt = u32_parse_eopt,
    174 	.print_eopt = u32_print_eopt,
    175 	.print_usage = u32_print_usage
    176 };
    177