/* * 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 */ %{ #include #include #include #include #include #include #include #include #include #include #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 ERROR LOGIC NOT OPERAND NUMBER ALIGN LAYER %token KW_OPEN "(" %token KW_CLOSE ")" %token KW_PLUS "+" %token KW_MASK "mask" %token KW_SHIFT ">>" %token KW_AT "at" %token EMATCH_CMP "cmp" %token EMATCH_NBYTE "pattern" %token EMATCH_TEXT "text" %token EMATCH_META "meta" %token KW_EQ "=" %token KW_GT ">" %token KW_LT "<" %token KW_FROM "from" %token KW_TO "to" %token META_RANDOM "random" %token META_LOADAVG_0 "loadavg_0" %token META_LOADAVG_1 "loadavg_1" %token META_LOADAVG_2 "loadavg_2" %token META_DEV "dev" %token META_PRIO "prio" %token META_PROTO "proto" %token META_PKTTYPE "pkttype" %token META_PKTLEN "pktlen" %token META_DATALEN "datalen" %token META_MACLEN "maclen" %token META_MARK "mark" %token META_TCINDEX "tcindex" %token META_RTCLASSID "rtclassid" %token META_RTIIF "rtiif" %token META_SK_FAMILY "sk_family" %token META_SK_STATE "sk_state" %token META_SK_REUSE "sk_reuse" %token META_SK_REFCNT "sk_refcnt" %token META_SK_RCVBUF "sk_rcvbuf" %token META_SK_SNDBUF "sk_sndbuf" %token META_SK_SHUTDOWN "sk_shutdown" %token META_SK_PROTO "sk_proto" %token META_SK_TYPE "sk_type" %token META_SK_RMEM_ALLOC "sk_rmem_alloc" %token META_SK_WMEM_ALLOC "sk_wmem_alloc" %token META_SK_WMEM_QUEUED "sk_wmem_queued" %token META_SK_RCV_QLEN "sk_rcv_qlen" %token META_SK_SND_QLEN "sk_snd_qlen" %token META_SK_ERR_QLEN "sk_err_qlen" %token META_SK_FORWARD_ALLOCS "sk_forward_allocs" %token META_SK_ALLOCS "sk_allocs" %token META_SK_ROUTE_CAPS "sk_route_caps" %token META_SK_HASH "sk_hash" %token META_SK_LINGERTIME "sk_lingertime" %token META_SK_ACK_BACKLOG "sk_ack_backlog" %token META_SK_MAX_ACK_BACKLOG "sk_max_ack_backlog" %token META_SK_PRIO "sk_prio" %token META_SK_RCVLOWAT "sk_rcvlowat" %token META_SK_RCVTIMEO "sk_rcvtimeo" %token META_SK_SNDTIMEO "sk_sndtimeo" %token META_SK_SENDMSG_OFF "sk_sendmsg_off" %token META_SK_WRITE_PENDING "sk_write_pending" %token META_VLAN "vlan" %token META_RXHASH "rxhash" %token META_DEVNAME "devname" %token META_SK_BOUND_IF "sk_bound_if" %token STR %token QUOTED %type align operand shift meta_int_id meta_var_id %type mask %type expr match ematch %type cmp_expr cmp_match %type pktloc text_from text_to %type pattern %type meta_value %destructor { free($$); NL_DBG(2, "string destructor\n"); } %destructor { rtnl_pktloc_put($$); NL_DBG(2, "pktloc destructor\n"); } %destructor { free($$.data); NL_DBG(2, "quoted destructor\n"); } %destructor { rtnl_meta_value_put($$); NL_DBG(2, "meta value destructor\n"); } %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(""); 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; } ;