From db5bd57899affbcaf42ac0b93c5c7be8f51ca390 Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Fri, 2 Jul 2010 14:06:59 +0200 Subject: Packet Location Interface --- Makefile.am | 3 + configure.in | 4 +- etc/pktloc | 44 +++++++++++ include/netlink-local.h | 1 + include/netlink/errno.h | 3 +- include/netlink/route/pktloc.h | 44 +++++++++++ lib/Makefile.am | 19 ++++- lib/error.c | 1 + lib/route/pktloc.c | 168 +++++++++++++++++++++++++++++++++++++++++ lib/route/pktloc_grammar.l | 42 +++++++++++ lib/route/pktloc_syntax.y | 108 ++++++++++++++++++++++++++ src/Makefile.am | 6 +- src/nl-pktloc-lookup.c | 37 +++++++++ 13 files changed, 475 insertions(+), 5 deletions(-) create mode 100644 etc/pktloc create mode 100644 include/netlink/route/pktloc.h create mode 100644 lib/route/pktloc.c create mode 100644 lib/route/pktloc_grammar.l create mode 100644 lib/route/pktloc_syntax.y create mode 100644 src/nl-pktloc-lookup.c diff --git a/Makefile.am b/Makefile.am index 01f9879..e1c47b9 100644 --- a/Makefile.am +++ b/Makefile.am @@ -6,6 +6,9 @@ SUBDIRS = include lib src doc pkgconfig_DATA = libnl-2.0.pc +sysconfdir = @sysconfdir@/libnl +sysconf_DATA = etc/pktloc + .PHONY: cscope cscope: cscope -b -q -R -Iinclude -slib -ssrc; diff --git a/configure.in b/configure.in index 02b5119..1fcdb64 100644 --- a/configure.in +++ b/configure.in @@ -6,7 +6,7 @@ # License as published by the Free Software Foundation version 2.1 # of the License. # -# Copyright (c) 2003-2008 Thomas Graf +# Copyright (c) 2003-2010 Thomas Graf # AC_INIT(libnl, 2.0, tgraf@suug.ch) @@ -18,6 +18,8 @@ AC_PROG_CC AM_PROG_CC_C_O AC_PROG_INSTALL AM_PROG_LIBTOOL +AM_PROG_LEX +AC_PROG_YACC AC_C_CONST AC_C_INLINE diff --git a/etc/pktloc b/etc/pktloc new file mode 100644 index 0000000..db36d40 --- /dev/null +++ b/etc/pktloc @@ -0,0 +1,44 @@ +# +# Location definitions for packet matching +# + +# name alignment offset mask +ip.version u8 net+0 0xF0 +ip.hdrlen u8 net+0 0x0F +ip.diffserv u8 net+1 +ip.length u16 net+2 +ip.id u16 net+4 +ip.df u8 net+6 0x40 +ip.mf u8 net+6 0x20 +ip.offset u16 net+6 0x1FFF +ip.ttl u8 net+8 +ip.proto u8 net+9 +ip.chksum u16 net+10 +ip.src u32 net+12 +ip.dst u32 net+16 + + +# +# Transmission Control Protocol (TCP) +# +# name alignment offset mask +tcp.sport u16 tcp+0 +tcp.dport u16 tcp+2 +tcp.seq u32 tcp+4 +tcp.ack u32 tcp+8 +tcp.off u8 tcp+12 0xF0 +tcp.reserved u8 tcp+12 0x0F +# FLAGS +tcp.win u16 tcp+14 +tcp.csum u16 tcp+16 +tcp.urg u16 tcp+18 +tcp.opts u32 tcp+20 + +# +# User Datagram Protocol (UDP) +# +# name alignment offset mask +udp.sport u16 tcp+0 +udp.dport u16 tcp+2 +udp.length u16 tcp+4 +udp.csum u16 tcp+6 diff --git a/include/netlink-local.h b/include/netlink-local.h index 1fafb51..e0c79db 100644 --- a/include/netlink-local.h +++ b/include/netlink-local.h @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include diff --git a/include/netlink/errno.h b/include/netlink/errno.h index 0b43da0..c8a376e 100644 --- a/include/netlink/errno.h +++ b/include/netlink/errno.h @@ -45,8 +45,9 @@ extern "C" { #define NLE_PROTO_MISMATCH 26 #define NLE_NOACCESS 27 #define NLE_PERM 28 +#define NLE_PKTLOC_FILE 29 -#define NLE_MAX NLE_PERM +#define NLE_MAX NLE_PKTLOC_FILE extern const char * nl_geterror(int); extern void nl_perror(int, const char *); diff --git a/include/netlink/route/pktloc.h b/include/netlink/route/pktloc.h new file mode 100644 index 0000000..28e1dc2 --- /dev/null +++ b/include/netlink/route/pktloc.h @@ -0,0 +1,44 @@ +/* + * netlink/route/pktloc.h Packet Location Aliasing + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * Copyright (c) 2010 Thomas Graf + */ + +#ifndef NETLINK_PKTLOC_H_ +#define NETLINK_PKTLOC_H_ + +#include +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct rtnl_pktloc +{ + char * name; + uint8_t align:4; + uint8_t layer:4; + uint8_t flags; + uint16_t offset; + uint32_t mask; + + struct nl_list_head list; +}; + +extern int rtnl_pktloc_lookup(const char *, struct rtnl_pktloc **); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/Makefile.am b/lib/Makefile.am index 1c2ae99..082408c 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -1,6 +1,6 @@ # -*- Makefile -*- -AM_CFLAGS = -Wall -I${top_srcdir}/include -D_GNU_SOURCE +AM_CFLAGS = -Wall -I${top_srcdir}/include -D_GNU_SOURCE -DSYSCONFDIR=\"$(sysconfdir)/libnl\" lib_LTLIBRARIES = \ libnl.la libnl-genl.la libnl-route.la libnl-nf.la @@ -23,6 +23,19 @@ libnl_nf_la_SOURCES = \ netfilter/netfilter.c netfilter/nfnl.c netfilter/queue.c \ netfilter/queue_msg.c netfilter/queue_msg_obj.c netfilter/queue_obj.c +BUILT_SOURCES = route/pktloc_syntax.h +CLEANFILES = \ + route/pktloc_grammar.c route/pktloc_grammar.h \ + route/pktloc_syntax.c route/pktloc_syntax.h + +# Hack to avoid using ylwrap. It does not function correctly in combination +# with --header-file= +route/pktloc_grammar.c: route/pktloc_grammar.l + $(LEX) --header-file=route/pktloc_grammar.h $(LFLAGS) -o $@ $^ + +route/pktloc_syntax.c: route/pktloc_syntax.y + $(YACC) -d $(YFLAGS) -o $@ $^ + libnl_route_la_LDFLAGS = -version-info 2:0:0 libnl_route_la_LIBADD = libnl.la libnl_route_la_SOURCES = \ @@ -40,4 +53,6 @@ libnl_route_la_SOURCES = \ route/sch/fifo.c route/sch/htb.c route/sch/netem.c route/sch/prio.c \ route/sch/red.c route/sch/sfq.c route/sch/tbf.c \ \ - fib_lookup/lookup.c fib_lookup/request.c + fib_lookup/lookup.c fib_lookup/request.c \ + \ + route/pktloc_syntax.c route/pktloc_grammar.c route/pktloc.c diff --git a/lib/error.c b/lib/error.c index b94a235..9a9fac7 100644 --- a/lib/error.c +++ b/lib/error.c @@ -42,6 +42,7 @@ static const char *errmsg[NLE_MAX+1] = { [NLE_PROTO_MISMATCH] = "Protocol mismatch", [NLE_NOACCESS] = "No Access", [NLE_PERM] = "Operation not permitted", +[NLE_PKTLOC_FILE] = "Unable to open packet location file", }; /** diff --git a/lib/route/pktloc.c b/lib/route/pktloc.c new file mode 100644 index 0000000..f0d0155 --- /dev/null +++ b/lib/route/pktloc.c @@ -0,0 +1,168 @@ +/* + * lib/route/pktloc.c Packet Location Aliasing + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2 of the License. + * + * Copyright (c) 2008-2010 Thomas Graf + */ + +/** + * @ingroup tc + * @defgroup pktloc Packet Location Aliasing + * Packet Location Aliasing + * + * The packet location aliasing interface eases the use of offset definitions + * inside packets by allowing them to be referenced by name. Known positions + * of protocol fields are stored in a configuration file and associated with + * a name for later reference. The configuration file is distributed with the + * library and provides a well defined set of definitions for most common + * protocol fields. + * + * @subsection pktloc_examples Examples + * @par Example 1.1 Looking up a packet location + * @code + * struct rtnl_pktloc *loc; + * + * rtnl_pktloc_lookup("ip.src", &loc); + * @endcode + * @{ + */ + +#include +#include +#include +#include +#include + +#include "pktloc_syntax.h" +#include "pktloc_grammar.h" + +/** @cond */ +#define PKTLOC_NAME_HT_SIZ 256 + +static struct nl_list_head pktloc_name_ht[PKTLOC_NAME_HT_SIZ]; + +/* djb2 */ +unsigned int pktloc_hash(const char *str) +{ + unsigned long hash = 5381; + int c; + + while ((c = *str++)) + hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ + + return hash % PKTLOC_NAME_HT_SIZ; +} + + +void rtnl_pktloc_add(struct rtnl_pktloc *loc) +{ + nl_list_add_tail(&loc->list, &pktloc_name_ht[pktloc_hash(loc->name)]); +} + +extern int pktloc_parse(void *scanner); + +/** @endcond */ + +static void rtnl_pktloc_free(struct rtnl_pktloc *loc) +{ + if (!loc) + return; + + free(loc->name); + free(loc); +} + +static int read_pktlocs(void) +{ + YY_BUFFER_STATE buf; + yyscan_t scanner = NULL; + static time_t last_read; + struct stat st = {0}; + char *path; + int i, err; + FILE *fd; + + asprintf(&path, "%s/pktloc", SYSCONFDIR); + + /* if stat fails, just try to read the file */ + if (stat(path, &st) == 0) { + /* Don't re-read file if file is unchanged */ + if (last_read == st.st_mtime) + return 0; + } + + if (!(fd = fopen(path, "r"))) + return -NLE_PKTLOC_FILE; + + for (i = 0; i < PKTLOC_NAME_HT_SIZ; i++) { + struct rtnl_pktloc *loc, *n; + + nl_list_for_each_entry_safe(loc, n, &pktloc_name_ht[i], list) + rtnl_pktloc_free(loc); + + nl_init_list_head(&pktloc_name_ht[i]); + } + + if ((err = pktloc_lex_init(&scanner)) < 0) + return -NLE_FAILURE; + + buf = pktloc__create_buffer(fd, YY_BUF_SIZE, scanner); + pktloc__switch_to_buffer(buf, scanner); + + if ((err = pktloc_parse(scanner)) < 0) + return -NLE_FAILURE; + + if (scanner) + pktloc_lex_destroy(scanner); + + free(path); + last_read = st.st_mtime; + + return 0; +} + +/** + * Lookup packet location alias + * @arg name Name of packet location. + * + * Tries to find a matching packet location alias for the supplied + * packet location name. + * + * The file containing the packet location definitions is automatically + * re-read if its modification time has changed since the last call. + * + * @return 0 on success or a negative error code. + * @retval NLE_PKTLOC_FILE Unable to open packet location file. + * @retval NLE_OBJ_NOTFOUND No matching packet location alias found. + */ +int rtnl_pktloc_lookup(const char *name, struct rtnl_pktloc **result) +{ + struct rtnl_pktloc *loc; + int hash, err; + + if ((err = read_pktlocs()) < 0) + return err; + + hash = pktloc_hash(name); + nl_list_for_each_entry(loc, &pktloc_name_ht[hash], list) { + if (!strcasecmp(loc->name, name)) { + *result = loc; + return 0; + } + } + + return -NLE_OBJ_NOTFOUND; +} + +static int __init pktloc_init(void) +{ + int i; + + for (i = 0; i < PKTLOC_NAME_HT_SIZ; i++) + nl_init_list_head(&pktloc_name_ht[i]); + + return 0; +} diff --git a/lib/route/pktloc_grammar.l b/lib/route/pktloc_grammar.l new file mode 100644 index 0000000..f710430 --- /dev/null +++ b/lib/route/pktloc_grammar.l @@ -0,0 +1,42 @@ +%{ + #include + #include + #include + #include + #include + #include "pktloc_syntax.h" +%} + +%option 8bit +%option reentrant +%option warn +%option noyywrap +%option nounput +%option bison-bridge +%option bison-locations +%option prefix="pktloc_" + +%% + +[ \t\r\n]+ + +"#".* + +[[:digit:]]+ | +0[xX][[:xdigit:]]+ { + yylval->i = strtoul(yytext, NULL, 0); + return NUMBER; + } + +"+" { return yylval->i = yytext[0]; } + +[lL][iI][nN][kK] { yylval->i = TCF_LAYER_LINK; return LAYER; } +[nN][eE][tT] { yylval->i = TCF_LAYER_NETWORK; return LAYER; } +[tT][cC][pP] { yylval->i = TCF_LAYER_TRANSPORT; return LAYER; } + +[^ \t\r\n+]+ { + yylval->s = strdup(yytext); + if (yylval->s == NULL) + return ERROR; + return NAME; + } diff --git a/lib/route/pktloc_syntax.y b/lib/route/pktloc_syntax.y new file mode 100644 index 0000000..05d609a --- /dev/null +++ b/lib/route/pktloc_syntax.y @@ -0,0 +1,108 @@ +%{ +#include +#include +#include +#include +#include +%} + +%locations +%error-verbose +%define api.pure +%name-prefix "pktloc_" + +%parse-param {void *scanner} +%lex-param {void *scanner} + +%union { + struct rtnl_pktloc *l; + uint32_t i; + char *s; +} + +%{ +extern int pktloc_lex(YYSTYPE *, YYLTYPE *, void *); +extern void rtnl_pktloc_add(struct rtnl_pktloc *); + +static void yyerror(YYLTYPE *locp, void *scanner, const char *msg) +{ + /* FIXME */ +} +%} + +%token ERROR NUMBER LAYER +%token NAME + +%type mask layer +%type location + +%destructor { free($$); } NAME + +%start input + +%% + +input: + def + { } + ; + +def: + /* empty */ + { } + | location def + { } + ; + +location: + NAME NAME layer NUMBER mask + { + struct rtnl_pktloc *loc; + + if (!(loc = calloc(1, sizeof(*loc)))) { + /* FIXME */ + } + + if (!strcasecmp($2, "u8")) + loc->align = TCF_EM_ALIGN_U8; + else if (!strcasecmp($2, "h8")) { + loc->align = TCF_EM_ALIGN_U8; + loc->flags = TCF_EM_CMP_TRANS; + } else if (!strcasecmp($2, "u16")) + loc->align = TCF_EM_ALIGN_U16; + else if (!strcasecmp($2, "h16")) { + loc->align = TCF_EM_ALIGN_U16; + loc->flags = TCF_EM_CMP_TRANS; + } else if (!strcasecmp($2, "u32")) + loc->align = TCF_EM_ALIGN_U32; + else if (!strcasecmp($2, "h32")) { + loc->align = TCF_EM_ALIGN_U32; + loc->flags = TCF_EM_CMP_TRANS; + } + + free($2); + + loc->name = $1; + loc->layer = $3; + loc->offset = $4; + loc->mask = $5; + + rtnl_pktloc_add(loc); + + $$ = loc; + } + ; + +layer: + /* empty */ + { $$ = TCF_LAYER_NETWORK; } + | LAYER '+' + { $$ = $1; } + ; + +mask: + /* empty */ + { $$ = 0; } + | NUMBER + { $$ = $1; } + ; diff --git a/src/Makefile.am b/src/Makefile.am index ca37843..4e2b57c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -20,7 +20,8 @@ noinst_PROGRAMS = \ nl-route-add nl-route-delete nl-route-get nl-route-list \ nl-fib-lookup \ nl-list-caches nl-list-sockets \ - nl-util-addr + nl-util-addr \ + nl-pktloc-lookup genl_ctrl_list_SOURCES = genl-ctrl-list.c genl_ctrl_list_LDADD = -lnl-genl -lnl-route @@ -95,3 +96,6 @@ nl_list_sockets_LDADD = -lnl-route nl_util_addr_SOURCES = nl-util-addr.c nl_util_addr_LDADD = -lnl-route + +nl_pktloc_lookup_SOURCES = nl-pktloc-lookup.c +nl_pktloc_lookup_LDADD = -lnl-route diff --git a/src/nl-pktloc-lookup.c b/src/nl-pktloc-lookup.c new file mode 100644 index 0000000..09b04b2 --- /dev/null +++ b/src/nl-pktloc-lookup.c @@ -0,0 +1,37 @@ +/* + * src/nl-pktloc-lookup.c Lookup packet location alias + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * Copyright (c) 2010 Thomas Graf + */ + +#include +#include + +static void print_usage(void) +{ + printf("Usage: nl-pktloc-lookup \n"); + exit(0); +} + +int main(int argc, char *argv[]) +{ + struct rtnl_pktloc *loc; + int err; + + if (argc < 2) + print_usage(); + + if ((err = rtnl_pktloc_lookup(argv[1], &loc)) < 0) + nl_cli_fatal(err, "Unable to lookup packet location: %s", + nl_geterror(err)); + + printf("%s: %u %u+%u 0x%x %u\n", loc->name, loc->align, + loc->layer, loc->offset, loc->mask, loc->flags); + + return 0; +} -- cgit v0.12