| /* |
| * lib/route/cls/ematch_syntax.y ematch expression syntax |
| * |
| * 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-2013 Thomas Graf <tgraf@suug.ch> |
| */ |
| |
| %{ |
| #include <netlink-private/netlink.h> |
| #include <netlink-private/tc.h> |
| #include <netlink/netlink.h> |
| #include <netlink/utils.h> |
| #include <netlink/route/pktloc.h> |
| #include <netlink/route/cls/ematch.h> |
| #include <netlink/route/cls/ematch/cmp.h> |
| #include <netlink/route/cls/ematch/nbyte.h> |
| #include <netlink/route/cls/ematch/text.h> |
| #include <netlink/route/cls/ematch/meta.h> |
| |
| #define META_ALLOC rtnl_meta_value_alloc_id |
| #define META_ID(name) TCF_META_ID_##name |
| #define META_INT TCF_META_TYPE_INT |
| #define META_VAR TCF_META_TYPE_VAR |
| %} |
| |
| %error-verbose |
| %define api.pure |
| %name-prefix "ematch_" |
| |
| %parse-param {void *scanner} |
| %parse-param {char **errp} |
| %parse-param {struct nl_list_head *root} |
| %lex-param {void *scanner} |
| |
| %union { |
| struct tcf_em_cmp cmp; |
| struct ematch_quoted q; |
| struct rtnl_ematch * e; |
| struct rtnl_pktloc * loc; |
| struct rtnl_meta_value *mv; |
| uint32_t i; |
| uint64_t i64; |
| char * s; |
| } |
| |
| %{ |
| extern int ematch_lex(YYSTYPE *, void *); |
| |
| static void yyerror(void *scanner, char **errp, struct nl_list_head *root, const char *msg) |
| { |
| if (msg) |
| *errp = strdup(msg); |
| else |
| *errp = NULL; |
| } |
| %} |
| |
| %token <i> ERROR LOGIC NOT OPERAND NUMBER ALIGN LAYER |
| %token <i> KW_OPEN "(" |
| %token <i> KW_CLOSE ")" |
| %token <i> KW_PLUS "+" |
| %token <i> KW_MASK "mask" |
| %token <i> KW_SHIFT ">>" |
| %token <i> KW_AT "at" |
| %token <i> EMATCH_CMP "cmp" |
| %token <i> EMATCH_NBYTE "pattern" |
| %token <i> EMATCH_TEXT "text" |
| %token <i> EMATCH_META "meta" |
| %token <i> KW_EQ "=" |
| %token <i> KW_GT ">" |
| %token <i> KW_LT "<" |
| %token <i> KW_FROM "from" |
| %token <i> KW_TO "to" |
| |
| %token <i> META_RANDOM "random" |
| %token <i> META_LOADAVG_0 "loadavg_0" |
| %token <i> META_LOADAVG_1 "loadavg_1" |
| %token <i> META_LOADAVG_2 "loadavg_2" |
| %token <i> META_DEV "dev" |
| %token <i> META_PRIO "prio" |
| %token <i> META_PROTO "proto" |
| %token <i> META_PKTTYPE "pkttype" |
| %token <i> META_PKTLEN "pktlen" |
| %token <i> META_DATALEN "datalen" |
| %token <i> META_MACLEN "maclen" |
| %token <i> META_MARK "mark" |
| %token <i> META_TCINDEX "tcindex" |
| %token <i> META_RTCLASSID "rtclassid" |
| %token <i> META_RTIIF "rtiif" |
| %token <i> META_SK_FAMILY "sk_family" |
| %token <i> META_SK_STATE "sk_state" |
| %token <i> META_SK_REUSE "sk_reuse" |
| %token <i> META_SK_REFCNT "sk_refcnt" |
| %token <i> META_SK_RCVBUF "sk_rcvbuf" |
| %token <i> META_SK_SNDBUF "sk_sndbuf" |
| %token <i> META_SK_SHUTDOWN "sk_shutdown" |
| %token <i> META_SK_PROTO "sk_proto" |
| %token <i> META_SK_TYPE "sk_type" |
| %token <i> META_SK_RMEM_ALLOC "sk_rmem_alloc" |
| %token <i> META_SK_WMEM_ALLOC "sk_wmem_alloc" |
| %token <i> META_SK_WMEM_QUEUED "sk_wmem_queued" |
| %token <i> META_SK_RCV_QLEN "sk_rcv_qlen" |
| %token <i> META_SK_SND_QLEN "sk_snd_qlen" |
| %token <i> META_SK_ERR_QLEN "sk_err_qlen" |
| %token <i> META_SK_FORWARD_ALLOCS "sk_forward_allocs" |
| %token <i> META_SK_ALLOCS "sk_allocs" |
| %token <i> META_SK_ROUTE_CAPS "sk_route_caps" |
| %token <i> META_SK_HASH "sk_hash" |
| %token <i> META_SK_LINGERTIME "sk_lingertime" |
| %token <i> META_SK_ACK_BACKLOG "sk_ack_backlog" |
| %token <i> META_SK_MAX_ACK_BACKLOG "sk_max_ack_backlog" |
| %token <i> META_SK_PRIO "sk_prio" |
| %token <i> META_SK_RCVLOWAT "sk_rcvlowat" |
| %token <i> META_SK_RCVTIMEO "sk_rcvtimeo" |
| %token <i> META_SK_SNDTIMEO "sk_sndtimeo" |
| %token <i> META_SK_SENDMSG_OFF "sk_sendmsg_off" |
| %token <i> META_SK_WRITE_PENDING "sk_write_pending" |
| %token <i> META_VLAN "vlan" |
| %token <i> META_RXHASH "rxhash" |
| %token <i> META_DEVNAME "devname" |
| %token <i> META_SK_BOUND_IF "sk_bound_if" |
| |
| %token <s> STR |
| |
| %token <q> QUOTED |
| |
| %type <i> align operand shift meta_int_id meta_var_id |
| %type <i64> mask |
| %type <e> expr match ematch |
| %type <cmp> cmp_expr cmp_match |
| %type <loc> pktloc text_from text_to |
| %type <q> pattern |
| %type <mv> meta_value |
| |
| %destructor { free($$); NL_DBG(2, "string destructor\n"); } <s> |
| %destructor { rtnl_pktloc_put($$); NL_DBG(2, "pktloc destructor\n"); } <loc> |
| %destructor { free($$.data); NL_DBG(2, "quoted destructor\n"); } <q> |
| %destructor { rtnl_meta_value_put($$); NL_DBG(2, "meta value destructor\n"); } <mv> |
| |
| %start input |
| |
| %% |
| |
| input: |
| /* empty */ |
| | expr |
| { |
| nl_list_add_tail(root, &$1->e_list); |
| } |
| ; |
| |
| expr: |
| match |
| { |
| $$ = $1; |
| } |
| | match LOGIC expr |
| { |
| rtnl_ematch_set_flags($1, $2); |
| |
| /* make ematch new head */ |
| nl_list_add_tail(&$1->e_list, &$3->e_list); |
| |
| $$ = $1; |
| } |
| ; |
| |
| match: |
| NOT ematch |
| { |
| rtnl_ematch_set_flags($2, TCF_EM_INVERT); |
| $$ = $2; |
| } |
| | ematch |
| { |
| $$ = $1; |
| } |
| ; |
| |
| ematch: |
| /* CMP */ |
| cmp_match |
| { |
| struct rtnl_ematch *e; |
| |
| if (!(e = rtnl_ematch_alloc())) { |
| *errp = strdup("Unable to allocate ematch object"); |
| YYABORT; |
| } |
| |
| if (rtnl_ematch_set_kind(e, TCF_EM_CMP) < 0) |
| BUG(); |
| |
| rtnl_ematch_cmp_set(e, &$1); |
| $$ = e; |
| } |
| | EMATCH_NBYTE "(" pktloc KW_EQ pattern ")" |
| { |
| struct rtnl_ematch *e; |
| |
| if (!(e = rtnl_ematch_alloc())) { |
| *errp = strdup("Unable to allocate ematch object"); |
| YYABORT; |
| } |
| |
| if (rtnl_ematch_set_kind(e, TCF_EM_NBYTE) < 0) |
| BUG(); |
| |
| rtnl_ematch_nbyte_set_offset(e, $3->layer, $3->offset); |
| rtnl_pktloc_put($3); |
| rtnl_ematch_nbyte_set_pattern(e, (uint8_t *) $5.data, $5.index); |
| |
| $$ = e; |
| } |
| | EMATCH_TEXT "(" STR QUOTED text_from text_to ")" |
| { |
| struct rtnl_ematch *e; |
| |
| if (!(e = rtnl_ematch_alloc())) { |
| *errp = strdup("Unable to allocate ematch object"); |
| YYABORT; |
| } |
| |
| if (rtnl_ematch_set_kind(e, TCF_EM_TEXT) < 0) |
| BUG(); |
| |
| rtnl_ematch_text_set_algo(e, $3); |
| rtnl_ematch_text_set_pattern(e, $4.data, $4.index); |
| |
| if ($5) { |
| rtnl_ematch_text_set_from(e, $5->layer, $5->offset); |
| rtnl_pktloc_put($5); |
| } |
| |
| if ($6) { |
| rtnl_ematch_text_set_to(e, $6->layer, $6->offset); |
| rtnl_pktloc_put($6); |
| } |
| |
| $$ = e; |
| } |
| | EMATCH_META "(" meta_value operand meta_value ")" |
| { |
| struct rtnl_ematch *e; |
| |
| if (!(e = rtnl_ematch_alloc())) { |
| *errp = strdup("Unable to allocate ematch object"); |
| YYABORT; |
| } |
| |
| if (rtnl_ematch_set_kind(e, TCF_EM_META) < 0) |
| BUG(); |
| |
| rtnl_ematch_meta_set_lvalue(e, $3); |
| rtnl_ematch_meta_set_rvalue(e, $5); |
| rtnl_ematch_meta_set_operand(e, $4); |
| |
| $$ = e; |
| } |
| /* CONTAINER */ |
| | "(" expr ")" |
| { |
| struct rtnl_ematch *e; |
| |
| if (!(e = rtnl_ematch_alloc())) { |
| *errp = strdup("Unable to allocate ematch object"); |
| YYABORT; |
| } |
| |
| if (rtnl_ematch_set_kind(e, TCF_EM_CONTAINER) < 0) |
| BUG(); |
| |
| /* Make e->childs the list head of a the ematch sequence */ |
| nl_list_add_tail(&e->e_childs, &$2->e_list); |
| |
| $$ = e; |
| } |
| ; |
| |
| /* |
| * CMP match |
| * |
| * match := cmp(expr) | expr |
| * expr := pktloc (=|>|<) NUMBER |
| * pktloc := alias | definition |
| * |
| */ |
| cmp_match: |
| EMATCH_CMP "(" cmp_expr ")" |
| { $$ = $3; } |
| | cmp_expr |
| { $$ = $1; } |
| ; |
| |
| cmp_expr: |
| pktloc operand NUMBER |
| { |
| if ($1->align == TCF_EM_ALIGN_U16 || |
| $1->align == TCF_EM_ALIGN_U32) |
| $$.flags = TCF_EM_CMP_TRANS; |
| |
| memset(&$$, 0, sizeof($$)); |
| |
| $$.mask = $1->mask; |
| $$.off = $1->offset; |
| $$.align = $1->align; |
| $$.layer = $1->layer; |
| $$.opnd = $2; |
| $$.val = $3; |
| |
| rtnl_pktloc_put($1); |
| } |
| ; |
| |
| text_from: |
| /* empty */ |
| { $$ = NULL; } |
| | "from" pktloc |
| { $$ = $2; } |
| ; |
| |
| text_to: |
| /* empty */ |
| { $$ = NULL; } |
| | "to" pktloc |
| { $$ = $2; } |
| ; |
| |
| meta_value: |
| QUOTED |
| { $$ = rtnl_meta_value_alloc_var($1.data, $1.len); } |
| | NUMBER |
| { $$ = rtnl_meta_value_alloc_int($1); } |
| | meta_int_id shift mask |
| { $$ = META_ALLOC(META_INT, $1, $2, $3); } |
| | meta_var_id shift |
| { $$ = META_ALLOC(META_VAR, $1, $2, 0); } |
| ; |
| |
| meta_int_id: |
| META_RANDOM { $$ = META_ID(RANDOM); } |
| |META_LOADAVG_0 { $$ = META_ID(LOADAVG_0); } |
| |META_LOADAVG_1 { $$ = META_ID(LOADAVG_1); } |
| |META_LOADAVG_2 { $$ = META_ID(LOADAVG_2); } |
| | META_DEV { $$ = META_ID(DEV); } |
| | META_PRIO { $$ = META_ID(PRIORITY); } |
| | META_PROTO { $$ = META_ID(PROTOCOL); } |
| | META_PKTTYPE { $$ = META_ID(PKTTYPE); } |
| | META_PKTLEN { $$ = META_ID(PKTLEN); } |
| | META_DATALEN { $$ = META_ID(DATALEN); } |
| | META_MACLEN { $$ = META_ID(MACLEN); } |
| | META_MARK { $$ = META_ID(NFMARK); } |
| | META_TCINDEX { $$ = META_ID(TCINDEX); } |
| | META_RTCLASSID { $$ = META_ID(RTCLASSID); } |
| | META_RTIIF { $$ = META_ID(RTIIF); } |
| | META_SK_FAMILY { $$ = META_ID(SK_FAMILY); } |
| | META_SK_STATE { $$ = META_ID(SK_STATE); } |
| | META_SK_REUSE { $$ = META_ID(SK_REUSE); } |
| | META_SK_REFCNT { $$ = META_ID(SK_REFCNT); } |
| | META_SK_RCVBUF { $$ = META_ID(SK_RCVBUF); } |
| | META_SK_SNDBUF { $$ = META_ID(SK_SNDBUF); } |
| | META_SK_SHUTDOWN { $$ = META_ID(SK_SHUTDOWN); } |
| | META_SK_PROTO { $$ = META_ID(SK_PROTO); } |
| | META_SK_TYPE { $$ = META_ID(SK_TYPE); } |
| | META_SK_RMEM_ALLOC { $$ = META_ID(SK_RMEM_ALLOC); } |
| | META_SK_WMEM_ALLOC { $$ = META_ID(SK_WMEM_ALLOC); } |
| | META_SK_WMEM_QUEUED { $$ = META_ID(SK_WMEM_QUEUED); } |
| | META_SK_RCV_QLEN { $$ = META_ID(SK_RCV_QLEN); } |
| | META_SK_SND_QLEN { $$ = META_ID(SK_SND_QLEN); } |
| | META_SK_ERR_QLEN { $$ = META_ID(SK_ERR_QLEN); } |
| | META_SK_FORWARD_ALLOCS { $$ = META_ID(SK_FORWARD_ALLOCS); } |
| | META_SK_ALLOCS { $$ = META_ID(SK_ALLOCS); } |
| | META_SK_ROUTE_CAPS { $$ = META_ID(SK_ROUTE_CAPS); } |
| | META_SK_HASH { $$ = META_ID(SK_HASH); } |
| | META_SK_LINGERTIME { $$ = META_ID(SK_LINGERTIME); } |
| | META_SK_ACK_BACKLOG { $$ = META_ID(SK_ACK_BACKLOG); } |
| | META_SK_MAX_ACK_BACKLOG { $$ = META_ID(SK_MAX_ACK_BACKLOG); } |
| | META_SK_PRIO { $$ = META_ID(SK_PRIO); } |
| | META_SK_RCVLOWAT { $$ = META_ID(SK_RCVLOWAT); } |
| | META_SK_RCVTIMEO { $$ = META_ID(SK_RCVTIMEO); } |
| | META_SK_SNDTIMEO { $$ = META_ID(SK_SNDTIMEO); } |
| | META_SK_SENDMSG_OFF { $$ = META_ID(SK_SENDMSG_OFF); } |
| | META_SK_WRITE_PENDING { $$ = META_ID(SK_WRITE_PENDING); } |
| | META_VLAN { $$ = META_ID(VLAN_TAG); } |
| | META_RXHASH { $$ = META_ID(RXHASH); } |
| ; |
| |
| meta_var_id: |
| META_DEVNAME { $$ = META_ID(DEV); } |
| | META_SK_BOUND_IF { $$ = META_ID(SK_BOUND_IF); } |
| ; |
| |
| /* |
| * pattern |
| */ |
| pattern: |
| QUOTED |
| { |
| $$ = $1; |
| } |
| | STR |
| { |
| struct nl_addr *addr; |
| |
| if (nl_addr_parse($1, AF_UNSPEC, &addr) == 0) { |
| $$.len = nl_addr_get_len(addr); |
| |
| $$.index = min_t(int, $$.len, nl_addr_get_prefixlen(addr)/8); |
| |
| if (!($$.data = calloc(1, $$.len))) { |
| nl_addr_put(addr); |
| YYABORT; |
| } |
| |
| memcpy($$.data, nl_addr_get_binary_addr(addr), $$.len); |
| nl_addr_put(addr); |
| } else { |
| if (asprintf(errp, "invalid pattern \"%s\"", $1) == -1) |
| *errp = NULL; |
| YYABORT; |
| } |
| } |
| ; |
| |
| /* |
| * packet location |
| */ |
| |
| pktloc: |
| STR |
| { |
| struct rtnl_pktloc *loc; |
| |
| if (rtnl_pktloc_lookup($1, &loc) < 0) { |
| if (asprintf(errp, "Packet location \"%s\" not found", $1) == -1) |
| *errp = NULL; |
| YYABORT; |
| } |
| |
| $$ = loc; |
| } |
| /* [u8|u16|u32|NUM at] LAYER + OFFSET [mask MASK] */ |
| | align LAYER "+" NUMBER mask |
| { |
| struct rtnl_pktloc *loc; |
| |
| if ($5 && (!$1 || $1 > TCF_EM_ALIGN_U32)) { |
| *errp = strdup("mask only allowed for alignments u8|u16|u32"); |
| YYABORT; |
| } |
| |
| if (!(loc = rtnl_pktloc_alloc())) { |
| *errp = strdup("Unable to allocate packet location object"); |
| YYABORT; |
| } |
| |
| loc->name = strdup("<USER-DEFINED>"); |
| loc->align = $1; |
| loc->layer = $2; |
| loc->offset = $4; |
| loc->mask = $5; |
| |
| $$ = loc; |
| } |
| ; |
| |
| align: |
| /* empty */ |
| { $$ = 0; } |
| | ALIGN "at" |
| { $$ = $1; } |
| | NUMBER "at" |
| { $$ = $1; } |
| ; |
| |
| mask: |
| /* empty */ |
| { $$ = 0; } |
| | KW_MASK NUMBER |
| { $$ = $2; } |
| ; |
| |
| shift: |
| /* empty */ |
| { $$ = 0; } |
| | KW_SHIFT NUMBER |
| { $$ = $2; } |
| ; |
| |
| operand: |
| KW_EQ |
| { $$ = TCF_EM_OPND_EQ; } |
| | KW_GT |
| { $$ = TCF_EM_OPND_GT; } |
| | KW_LT |
| { $$ = TCF_EM_OPND_LT; } |
| ; |