1*f885c9d9Sflorian /* $OpenBSD: parser.c,v 1.137 2024/08/22 08:17:54 florian Exp $ */ 2acd8015aShenning 3acd8015aShenning /* 4acd8015aShenning * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> 5280f24a4Sphessler * Copyright (c) 2016 Job Snijders <job@instituut.net> 6280f24a4Sphessler * Copyright (c) 2016 Peter Hessler <phessler@openbsd.org> 7acd8015aShenning * 8acd8015aShenning * Permission to use, copy, modify, and distribute this software for any 9acd8015aShenning * purpose with or without fee is hereby granted, provided that the above 10acd8015aShenning * copyright notice and this permission notice appear in all copies. 11acd8015aShenning * 12acd8015aShenning * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 13acd8015aShenning * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 14acd8015aShenning * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 15acd8015aShenning * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 16acd8015aShenning * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 17acd8015aShenning * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 18acd8015aShenning * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 19acd8015aShenning */ 20acd8015aShenning 21fc210fe3Sclaudio #include <sys/types.h> 22fc210fe3Sclaudio 23cb39b155Sclaudio #include <endian.h> 24d50a3b9fSclaudio #include <err.h> 257b6c56a0Sclaudio #include <errno.h> 26a20554fdSclaudio #include <fcntl.h> 277b6c56a0Sclaudio #include <limits.h> 28fc210fe3Sclaudio #include <netdb.h> 29acd8015aShenning #include <stdio.h> 307b6c56a0Sclaudio #include <stdlib.h> 31acd8015aShenning #include <string.h> 322302052bShenning #include <unistd.h> 33acd8015aShenning 34acd8015aShenning #include "parser.h" 35acd8015aShenning 36acd8015aShenning enum token_type { 37acd8015aShenning ENDTOKEN, 38d1d41292Sclaudio NOTOKEN, 39d1d41292Sclaudio ANYTOKEN, 40acd8015aShenning KEYWORD, 41acd8015aShenning ADDRESS, 42f72661b1Sclaudio PEERADDRESS, 437b6c56a0Sclaudio FLAG, 447b6c56a0Sclaudio ASNUM, 45d50a3b9fSclaudio ASTYPE, 46fd71c51dShenning PREFIX, 479caec0aeSclaudio PEERDESC, 48903de74aSclaudio GROUPDESC, 49a9a0337fSclaudio RIBNAME, 50a78f83ceSderaadt COMMUNICATION, 519caec0aeSclaudio COMMUNITY, 52e3bd233eSbenno EXTCOMMUNITY, 53bddeed9cSclaudio LRGCOMMUNITY, 549caec0aeSclaudio LOCALPREF, 559caec0aeSclaudio MED, 569caec0aeSclaudio NEXTHOP, 579caec0aeSclaudio PFTABLE, 589caec0aeSclaudio PREPNBR, 599caec0aeSclaudio PREPSELF, 60c41f8a59Shenning WEIGHT, 61a73789d3Sclaudio RD, 622302052bShenning FAMILY, 63a20554fdSclaudio RTABLE, 6446daf8ccSclaudio FILENAME, 6546daf8ccSclaudio PATHID, 66e3925e28Sclaudio FLOW_PROTO, 67e3925e28Sclaudio FLOW_SRC, 68e3925e28Sclaudio FLOW_DST, 69e3925e28Sclaudio FLOW_SRCPORT, 70e3925e28Sclaudio FLOW_DSTPORT, 71e3925e28Sclaudio FLOW_ICMPTYPE, 72e3925e28Sclaudio FLOW_ICMPCODE, 73e3925e28Sclaudio FLOW_LENGTH, 74e3925e28Sclaudio FLOW_DSCP, 75e3925e28Sclaudio FLOW_FLAGS, 76e3925e28Sclaudio FLOW_FRAGS, 772302052bShenning }; 782302052bShenning 79acd8015aShenning struct token { 80acd8015aShenning enum token_type type; 81acd8015aShenning const char *keyword; 82acd8015aShenning int value; 83acd8015aShenning const struct token *next; 84acd8015aShenning }; 85acd8015aShenning 86d1d41292Sclaudio static const struct token *prevtable; 87d1d41292Sclaudio 88acd8015aShenning static const struct token t_main[]; 89acd8015aShenning static const struct token t_show[]; 9016fcf8d2Shenning static const struct token t_show_summary[]; 91acd8015aShenning static const struct token t_show_fib[]; 927b6c56a0Sclaudio static const struct token t_show_rib[]; 93bfee927aSclaudio static const struct token t_show_avs[]; 94854886c9Sdenis static const struct token t_show_ovs[]; 95a20554fdSclaudio static const struct token t_show_mrt[]; 96a20554fdSclaudio static const struct token t_show_mrt_file[]; 97f72661b1Sclaudio static const struct token t_show_rib_neigh[]; 98a20554fdSclaudio static const struct token t_show_mrt_neigh[]; 99a9a0337fSclaudio static const struct token t_show_rib_rib[]; 100acd8015aShenning static const struct token t_show_neighbor[]; 101acd8015aShenning static const struct token t_show_neighbor_modifiers[]; 102acd8015aShenning static const struct token t_fib[]; 103acd8015aShenning static const struct token t_neighbor[]; 104acd8015aShenning static const struct token t_neighbor_modifiers[]; 105a20554fdSclaudio static const struct token t_show_rib_as[]; 106a20554fdSclaudio static const struct token t_show_mrt_as[]; 107d50a3b9fSclaudio static const struct token t_show_prefix[]; 1087b6c56a0Sclaudio static const struct token t_show_ip[]; 1099caec0aeSclaudio static const struct token t_network[]; 1105fed6b04Sclaudio static const struct token t_flowspec[]; 111e3925e28Sclaudio static const struct token t_flowfamily[]; 112e3925e28Sclaudio static const struct token t_flowrule[]; 113e3925e28Sclaudio static const struct token t_flowsrc[]; 114e3925e28Sclaudio static const struct token t_flowdst[]; 115e3925e28Sclaudio static const struct token t_flowsrcport[]; 116e3925e28Sclaudio static const struct token t_flowdstport[]; 117e3925e28Sclaudio static const struct token t_flowicmp[]; 118325664f1Sclaudio static const struct token t_bulk[]; 1192fb3b566Shenning static const struct token t_network_show[]; 120a4cb7d77Sclaudio static const struct token t_prefix[]; 1219caec0aeSclaudio static const struct token t_set[]; 1229caec0aeSclaudio static const struct token t_nexthop[]; 1239caec0aeSclaudio static const struct token t_pftable[]; 124c3319070Sclaudio static const struct token t_log[]; 125a78f83ceSderaadt static const struct token t_communication[]; 126acd8015aShenning 127acd8015aShenning static const struct token t_main[] = { 128acd8015aShenning { KEYWORD, "fib", FIB, t_fib}, 1295fed6b04Sclaudio { KEYWORD, "flowspec", NONE, t_flowspec}, 1303b433cf9Sclaudio { KEYWORD, "log", NONE, t_log}, 131acd8015aShenning { KEYWORD, "neighbor", NEIGHBOR, t_neighbor}, 1329caec0aeSclaudio { KEYWORD, "network", NONE, t_network}, 1333b433cf9Sclaudio { KEYWORD, "reload", RELOAD, t_communication}, 1343b433cf9Sclaudio { KEYWORD, "show", SHOW, t_show}, 135acd8015aShenning { ENDTOKEN, "", NONE, NULL} 136acd8015aShenning }; 137acd8015aShenning 138acd8015aShenning static const struct token t_show[] = { 139acd8015aShenning { NOTOKEN, "", NONE, NULL}, 140acd8015aShenning { KEYWORD, "fib", SHOW_FIB, t_show_fib}, 1415fed6b04Sclaudio { KEYWORD, "flowspec", FLOWSPEC_SHOW, t_network_show}, 142acd8015aShenning { KEYWORD, "interfaces", SHOW_INTERFACE, NULL}, 1433b433cf9Sclaudio { KEYWORD, "ip", NONE, t_show_ip}, 1443b433cf9Sclaudio { KEYWORD, "metrics", SHOW_METRICS, NULL}, 1453b433cf9Sclaudio { KEYWORD, "mrt", SHOW_MRT, t_show_mrt}, 146acd8015aShenning { KEYWORD, "neighbor", SHOW_NEIGHBOR, t_show_neighbor}, 147c49d37b1Shenning { KEYWORD, "network", NETWORK_SHOW, t_network_show}, 148acd8015aShenning { KEYWORD, "nexthop", SHOW_NEXTHOP, NULL}, 1497b6c56a0Sclaudio { KEYWORD, "rib", SHOW_RIB, t_show_rib}, 15069d2b5abSclaudio { KEYWORD, "rtr", SHOW_RTR, NULL}, 1513b433cf9Sclaudio { KEYWORD, "sets", SHOW_SET, NULL}, 1523b433cf9Sclaudio { KEYWORD, "summary", SHOW_SUMMARY, t_show_summary}, 1533b433cf9Sclaudio { KEYWORD, "tables", SHOW_FIB_TABLES, NULL}, 15416fcf8d2Shenning { ENDTOKEN, "", NONE, NULL} 15516fcf8d2Shenning }; 15616fcf8d2Shenning 15716fcf8d2Shenning static const struct token t_show_summary[] = { 15816fcf8d2Shenning { NOTOKEN, "", NONE, NULL}, 15916fcf8d2Shenning { KEYWORD, "terse", SHOW_SUMMARY_TERSE, NULL}, 160acd8015aShenning { ENDTOKEN, "", NONE, NULL} 161acd8015aShenning }; 162acd8015aShenning 163acd8015aShenning static const struct token t_show_fib[] = { 164acd8015aShenning { NOTOKEN, "", NONE, NULL}, 165ef0c8610Sclaudio { FLAG, "bgp", F_BGPD, t_show_fib}, 1663b433cf9Sclaudio { FLAG, "connected", F_CONNECTED, t_show_fib}, 167acd8015aShenning { FLAG, "nexthop", F_NEXTHOP, t_show_fib}, 1683b433cf9Sclaudio { FLAG, "static", F_STATIC, t_show_fib}, 1697ddd74cdSclaudio { RTABLE, "table", NONE, t_show_fib}, 170c41f8a59Shenning { FAMILY, "", NONE, t_show_fib}, 171acd8015aShenning { ADDRESS, "", NONE, NULL}, 172acd8015aShenning { ENDTOKEN, "", NONE, NULL} 173acd8015aShenning }; 174acd8015aShenning 1757b6c56a0Sclaudio static const struct token t_show_rib[] = { 1767b6c56a0Sclaudio { NOTOKEN, "", NONE, NULL}, 177a20554fdSclaudio { ASTYPE, "as", AS_ALL, t_show_rib_as}, 178bfee927aSclaudio { KEYWORD, "avs", NONE, t_show_avs}, 1793b433cf9Sclaudio { FLAG, "best", F_CTL_BEST, t_show_rib}, 180bddeed9cSclaudio { COMMUNITY, "community", NONE, t_show_rib}, 1813b433cf9Sclaudio { FLAG, "detail", F_CTL_DETAIL, t_show_rib}, 1824ae31d17Sclaudio { FLAG, "disqualified", F_CTL_INELIGIBLE, t_show_rib}, 1833b433cf9Sclaudio { ASTYPE, "empty-as", AS_EMPTY, t_show_rib}, 1843b433cf9Sclaudio { FLAG, "error", F_CTL_INVALID, t_show_rib}, 185bddeed9cSclaudio { EXTCOMMUNITY, "ext-community", NONE, t_show_rib}, 18609b2f24cSclaudio { FLAG, "filtered", F_CTL_FILTERED, t_show_rib}, 1873b433cf9Sclaudio { FLAG, "in", F_CTL_ADJ_IN, t_show_rib}, 188bddeed9cSclaudio { LRGCOMMUNITY, "large-community", NONE, t_show_rib}, 1893b433cf9Sclaudio { FLAG, "leaked", F_CTL_LEAKED, t_show_rib}, 1903b433cf9Sclaudio { KEYWORD, "memory", SHOW_RIB_MEM, NULL}, 1913b433cf9Sclaudio { KEYWORD, "neighbor", NONE, t_show_rib_neigh}, 1923b433cf9Sclaudio { FLAG, "out", F_CTL_ADJ_OUT, t_show_rib}, 19346daf8ccSclaudio { KEYWORD, "ovs", NONE, t_show_ovs}, 1947ddd74cdSclaudio { PATHID, "path-id", NONE, t_show_rib}, 1953b433cf9Sclaudio { ASTYPE, "peer-as", AS_PEER, t_show_rib_as}, 1963b433cf9Sclaudio { FLAG, "selected", F_CTL_BEST, t_show_rib}, 1973b433cf9Sclaudio { ASTYPE, "source-as", AS_SOURCE, t_show_rib_as}, 1983b433cf9Sclaudio { FLAG, "ssv", F_CTL_SSV, t_show_rib}, 1995901a216Sclaudio { KEYWORD, "summary", SHOW_SUMMARY, t_show_summary}, 2003b433cf9Sclaudio { KEYWORD, "table", NONE, t_show_rib_rib}, 2013b433cf9Sclaudio { ASTYPE, "transit-as", AS_TRANSIT, t_show_rib_as}, 202f72661b1Sclaudio { FAMILY, "", NONE, t_show_rib}, 2036ed5da00Sclaudio { PREFIX, "", NONE, t_show_prefix}, 2047b6c56a0Sclaudio { ENDTOKEN, "", NONE, NULL} 2057b6c56a0Sclaudio }; 2067b6c56a0Sclaudio 207bfee927aSclaudio static const struct token t_show_avs[] = { 208bfee927aSclaudio { FLAG, "invalid", F_CTL_AVS_INVALID, t_show_rib}, 2099a717891Sjob { FLAG, "unknown", F_CTL_AVS_UNKNOWN, t_show_rib}, 2103b433cf9Sclaudio { FLAG, "valid" , F_CTL_AVS_VALID, t_show_rib}, 211bfee927aSclaudio { ENDTOKEN, "", NONE, NULL} 212bfee927aSclaudio }; 213bfee927aSclaudio 214854886c9Sdenis static const struct token t_show_ovs[] = { 215854886c9Sdenis { FLAG, "invalid", F_CTL_OVS_INVALID, t_show_rib}, 216854886c9Sdenis { FLAG, "not-found", F_CTL_OVS_NOTFOUND, t_show_rib}, 2173b433cf9Sclaudio { FLAG, "valid" , F_CTL_OVS_VALID, t_show_rib}, 218854886c9Sdenis { ENDTOKEN, "", NONE, NULL} 219854886c9Sdenis }; 220a20554fdSclaudio 221a20554fdSclaudio static const struct token t_show_mrt[] = { 222a20554fdSclaudio { NOTOKEN, "", NONE, NULL}, 223a20554fdSclaudio { ASTYPE, "as", AS_ALL, t_show_mrt_as}, 224a20554fdSclaudio { FLAG, "detail", F_CTL_DETAIL, t_show_mrt}, 2253b433cf9Sclaudio { ASTYPE, "empty-as", AS_EMPTY, t_show_mrt}, 226a20554fdSclaudio { KEYWORD, "file", NONE, t_show_mrt_file}, 2273b433cf9Sclaudio { KEYWORD, "neighbor", NONE, t_show_mrt_neigh}, 2283b433cf9Sclaudio { ASTYPE, "peer-as", AS_PEER, t_show_mrt_as}, 2293b433cf9Sclaudio { FLAG, "peers", F_CTL_NEIGHBORS,t_show_mrt}, 2303b433cf9Sclaudio { ASTYPE, "source-as", AS_SOURCE, t_show_mrt_as}, 2313b433cf9Sclaudio { FLAG, "ssv", F_CTL_SSV, t_show_mrt}, 2323b433cf9Sclaudio { ASTYPE, "transit-as", AS_TRANSIT, t_show_mrt_as}, 233a20554fdSclaudio { FAMILY, "", NONE, t_show_mrt}, 234a20554fdSclaudio { PREFIX, "", NONE, t_show_prefix}, 235a20554fdSclaudio { ENDTOKEN, "", NONE, NULL} 236a20554fdSclaudio }; 237a20554fdSclaudio 238a20554fdSclaudio static const struct token t_show_mrt_file[] = { 239a20554fdSclaudio { FILENAME, "", NONE, t_show_mrt}, 240a20554fdSclaudio { ENDTOKEN, "", NONE, NULL} 241a20554fdSclaudio }; 242a20554fdSclaudio 243903de74aSclaudio static const struct token t_show_rib_neigh_group[] = { 244903de74aSclaudio { GROUPDESC, "", NONE, t_show_rib}, 245903de74aSclaudio { ENDTOKEN, "", NONE, NULL} 246903de74aSclaudio }; 247903de74aSclaudio 248f72661b1Sclaudio static const struct token t_show_rib_neigh[] = { 249903de74aSclaudio { KEYWORD, "group", NONE, t_show_rib_neigh_group}, 250f72661b1Sclaudio { PEERADDRESS, "", NONE, t_show_rib}, 251f72661b1Sclaudio { PEERDESC, "", NONE, t_show_rib}, 252f72661b1Sclaudio { ENDTOKEN, "", NONE, NULL} 253f72661b1Sclaudio }; 254f72661b1Sclaudio 255a20554fdSclaudio static const struct token t_show_mrt_neigh[] = { 256a20554fdSclaudio { PEERADDRESS, "", NONE, t_show_mrt}, 257a20554fdSclaudio { ENDTOKEN, "", NONE, NULL} 258a20554fdSclaudio }; 259a20554fdSclaudio 260a9a0337fSclaudio static const struct token t_show_rib_rib[] = { 261a9a0337fSclaudio { RIBNAME, "", NONE, t_show_rib}, 262a9a0337fSclaudio { ENDTOKEN, "", NONE, NULL} 263a9a0337fSclaudio }; 264a9a0337fSclaudio 265acd8015aShenning static const struct token t_show_neighbor_modifiers[] = { 266acd8015aShenning { NOTOKEN, "", NONE, NULL}, 267acd8015aShenning { KEYWORD, "messages", SHOW_NEIGHBOR, NULL}, 2685901a216Sclaudio { KEYWORD, "terse", SHOW_NEIGHBOR_TERSE, NULL}, 2693b433cf9Sclaudio { KEYWORD, "timers", SHOW_NEIGHBOR_TIMERS, NULL}, 270acd8015aShenning { ENDTOKEN, "", NONE, NULL} 271acd8015aShenning }; 272acd8015aShenning 273903de74aSclaudio static const struct token t_show_neighbor_group[] = { 274903de74aSclaudio { GROUPDESC, "", NONE, t_show_neighbor_modifiers}, 275903de74aSclaudio { ENDTOKEN, "", NONE, NULL} 276903de74aSclaudio }; 277903de74aSclaudio 278903de74aSclaudio static const struct token t_show_neighbor[] = { 279903de74aSclaudio { NOTOKEN, "", NONE, NULL}, 280903de74aSclaudio { KEYWORD, "group", NONE, t_show_neighbor_group}, 281903de74aSclaudio { PEERADDRESS, "", NONE, t_show_neighbor_modifiers}, 282903de74aSclaudio { PEERDESC, "", NONE, t_show_neighbor_modifiers}, 283903de74aSclaudio { ENDTOKEN, "", NONE, NULL} 284903de74aSclaudio }; 285903de74aSclaudio 286acd8015aShenning static const struct token t_fib[] = { 287acd8015aShenning { KEYWORD, "couple", FIB_COUPLE, NULL}, 288acd8015aShenning { KEYWORD, "decouple", FIB_DECOUPLE, NULL}, 2897ddd74cdSclaudio { RTABLE, "table", NONE, t_fib}, 290acd8015aShenning { ENDTOKEN, "", NONE, NULL} 291acd8015aShenning }; 292acd8015aShenning 293903de74aSclaudio static const struct token t_neighbor_group[] = { 294903de74aSclaudio { GROUPDESC, "", NONE, t_neighbor_modifiers}, 295903de74aSclaudio { ENDTOKEN, "", NONE, NULL} 296903de74aSclaudio }; 297903de74aSclaudio 298acd8015aShenning static const struct token t_neighbor[] = { 299903de74aSclaudio { KEYWORD, "group", NONE, t_neighbor_group}, 300f72661b1Sclaudio { PEERADDRESS, "", NONE, t_neighbor_modifiers}, 301fd71c51dShenning { PEERDESC, "", NONE, t_neighbor_modifiers}, 302acd8015aShenning { ENDTOKEN, "", NONE, NULL} 303acd8015aShenning }; 304acd8015aShenning 305a78f83ceSderaadt static const struct token t_communication[] = { 3060561b344Sphessler { NOTOKEN, "", NONE, NULL}, 307a78f83ceSderaadt { COMMUNICATION, "", NONE, NULL}, 3080561b344Sphessler { ENDTOKEN, "", NONE, NULL} 3090561b344Sphessler }; 3100561b344Sphessler 311acd8015aShenning static const struct token t_neighbor_modifiers[] = { 312a78f83ceSderaadt { KEYWORD, "clear", NEIGHBOR_CLEAR, t_communication}, 31307820ff6Sclaudio { KEYWORD, "destroy", NEIGHBOR_DESTROY, NULL}, 3143b433cf9Sclaudio { KEYWORD, "down", NEIGHBOR_DOWN, t_communication}, 3153b433cf9Sclaudio { KEYWORD, "refresh", NEIGHBOR_RREFRESH, NULL}, 3163b433cf9Sclaudio { KEYWORD, "up", NEIGHBOR_UP, NULL}, 317acd8015aShenning { ENDTOKEN, "", NONE, NULL} 318acd8015aShenning }; 319acd8015aShenning 320a20554fdSclaudio static const struct token t_show_rib_as[] = { 321f72661b1Sclaudio { ASNUM, "", NONE, t_show_rib}, 3227b6c56a0Sclaudio { ENDTOKEN, "", NONE, NULL} 3237b6c56a0Sclaudio }; 3247b6c56a0Sclaudio 325a20554fdSclaudio static const struct token t_show_mrt_as[] = { 326a20554fdSclaudio { ASNUM, "", NONE, t_show_mrt}, 327a20554fdSclaudio { ENDTOKEN, "", NONE, NULL} 328a20554fdSclaudio }; 329a20554fdSclaudio 330d50a3b9fSclaudio static const struct token t_show_prefix[] = { 331d1d41292Sclaudio { FLAG, "all", F_LONGER, t_show_rib}, 332d1d41292Sclaudio { FLAG, "longer-prefixes", F_LONGER, t_show_rib}, 333d1d41292Sclaudio { FLAG, "or-longer", F_LONGER, t_show_rib}, 334d1d41292Sclaudio { FLAG, "or-shorter", F_SHORTER, t_show_rib}, 335d1d41292Sclaudio { ANYTOKEN, "", NONE, t_show_rib}, 336d50a3b9fSclaudio { ENDTOKEN, "", NONE, NULL} 337d50a3b9fSclaudio }; 338d50a3b9fSclaudio 3397b6c56a0Sclaudio static const struct token t_show_ip[] = { 3407b6c56a0Sclaudio { KEYWORD, "bgp", SHOW_RIB, t_show_rib}, 3417b6c56a0Sclaudio { ENDTOKEN, "", NONE, NULL} 3427b6c56a0Sclaudio }; 3437b6c56a0Sclaudio 3449caec0aeSclaudio static const struct token t_network[] = { 345a4cb7d77Sclaudio { KEYWORD, "add", NETWORK_ADD, t_prefix}, 3463b433cf9Sclaudio { KEYWORD, "bulk", NONE, t_bulk}, 347a4cb7d77Sclaudio { KEYWORD, "delete", NETWORK_REMOVE, t_prefix}, 348a4cb7d77Sclaudio { KEYWORD, "flush", NETWORK_FLUSH, NULL}, 349f486926aSclaudio { KEYWORD, "mrt", NETWORK_MRT, t_show_mrt}, 3503b433cf9Sclaudio { KEYWORD, "show", NETWORK_SHOW, t_network_show}, 351325664f1Sclaudio { ENDTOKEN, "", NONE, NULL} 352325664f1Sclaudio }; 353325664f1Sclaudio 3545fed6b04Sclaudio static const struct token t_flowspec[] = { 355e3925e28Sclaudio { KEYWORD, "add", FLOWSPEC_ADD, t_flowfamily}, 356e3925e28Sclaudio { KEYWORD, "delete", FLOWSPEC_REMOVE,t_flowfamily}, 3575fed6b04Sclaudio { KEYWORD, "flush", FLOWSPEC_FLUSH, NULL}, 3585fed6b04Sclaudio { KEYWORD, "show", FLOWSPEC_SHOW, t_network_show}, 3595fed6b04Sclaudio { ENDTOKEN, "", NONE, NULL} 3605fed6b04Sclaudio }; 3615fed6b04Sclaudio 362e3925e28Sclaudio static const struct token t_flowfamily[] = { 363e3925e28Sclaudio { FAMILY, "", NONE, t_flowrule}, 364e3925e28Sclaudio { ENDTOKEN, "", NONE, NULL} 365e3925e28Sclaudio }; 366e3925e28Sclaudio 367e3925e28Sclaudio static const struct token t_flowrule[] = { 368e3925e28Sclaudio { NOTOKEN, "", NONE, NULL}, 369e3925e28Sclaudio { FLOW_FLAGS, "flags", NONE, t_flowrule}, 370e3925e28Sclaudio { FLOW_FRAGS, "fragment", NONE, t_flowrule}, 371e3925e28Sclaudio { KEYWORD, "from", NONE, t_flowsrc}, 372e3925e28Sclaudio { FLOW_ICMPTYPE,"icmp-type", NONE, t_flowicmp}, 373e3925e28Sclaudio { FLOW_LENGTH, "length", NONE, t_flowrule}, 374e3925e28Sclaudio { FLOW_PROTO, "proto", NONE, t_flowrule}, 375e3925e28Sclaudio { KEYWORD, "set", NONE, t_set}, 376e3925e28Sclaudio { KEYWORD, "to", NONE, t_flowdst}, 377e3925e28Sclaudio { FLOW_DSCP, "dscp", NONE, t_flowrule}, 378e3925e28Sclaudio { ENDTOKEN, "", NONE, NULL} 379e3925e28Sclaudio }; 380e3925e28Sclaudio 381e3925e28Sclaudio static const struct token t_flowsrc[] = { 382e3925e28Sclaudio { KEYWORD, "any", NONE, t_flowsrcport}, 383e3925e28Sclaudio { FLOW_SRC, "", NONE, t_flowsrcport}, 384e3925e28Sclaudio { ENDTOKEN, "", NONE, NULL} 385e3925e28Sclaudio }; 386e3925e28Sclaudio 387e3925e28Sclaudio static const struct token t_flowdst[] = { 388e3925e28Sclaudio { KEYWORD, "any", NONE, t_flowdstport}, 389e3925e28Sclaudio { FLOW_DST, "", NONE, t_flowdstport}, 390e3925e28Sclaudio { ENDTOKEN, "", NONE, NULL} 391e3925e28Sclaudio }; 392e3925e28Sclaudio 393e3925e28Sclaudio static const struct token t_flowsrcport[] = { 394e3925e28Sclaudio { FLOW_SRCPORT, "port", NONE, t_flowrule}, 395e3925e28Sclaudio { ANYTOKEN, "", NONE, t_flowrule}, 396e3925e28Sclaudio { ENDTOKEN, "", NONE, NULL} 397e3925e28Sclaudio }; 398e3925e28Sclaudio 399e3925e28Sclaudio static const struct token t_flowdstport[] = { 400e3925e28Sclaudio { FLOW_DSTPORT, "port", NONE, t_flowrule}, 401e3925e28Sclaudio { ANYTOKEN, "", NONE, t_flowrule}, 402e3925e28Sclaudio { ENDTOKEN, "", NONE, NULL} 403e3925e28Sclaudio }; 404e3925e28Sclaudio 405e3925e28Sclaudio static const struct token t_flowicmp[] = { 406e3925e28Sclaudio { FLOW_ICMPCODE,"code", NONE, t_flowrule}, 407e3925e28Sclaudio { ANYTOKEN, "", NONE, t_flowrule}, 408e3925e28Sclaudio { ENDTOKEN, "", NONE, NULL} 409e3925e28Sclaudio }; 410e3925e28Sclaudio 411325664f1Sclaudio static const struct token t_bulk[] = { 412325664f1Sclaudio { KEYWORD, "add", NETWORK_BULK_ADD, t_set}, 413325664f1Sclaudio { KEYWORD, "delete", NETWORK_BULK_REMOVE, NULL}, 414a4cb7d77Sclaudio { ENDTOKEN, "", NONE, NULL} 415a4cb7d77Sclaudio }; 416a4cb7d77Sclaudio 417a4cb7d77Sclaudio static const struct token t_prefix[] = { 4189caec0aeSclaudio { PREFIX, "", NONE, t_set}, 419a4cb7d77Sclaudio { ENDTOKEN, "", NONE, NULL} 420a4cb7d77Sclaudio }; 421a4cb7d77Sclaudio 4222fb3b566Shenning static const struct token t_network_show[] = { 4232fb3b566Shenning { NOTOKEN, "", NONE, NULL}, 4242fb3b566Shenning { FAMILY, "", NONE, NULL}, 4252fb3b566Shenning { ENDTOKEN, "", NONE, NULL} 4262fb3b566Shenning }; 4272fb3b566Shenning 428a73789d3Sclaudio static const struct token t_rd[] = { 429a73789d3Sclaudio { RD, "", NONE, t_set}, 430a73789d3Sclaudio { ENDTOKEN, "", NONE, NULL} 431a73789d3Sclaudio }; 432a73789d3Sclaudio 4339caec0aeSclaudio static const struct token t_set[] = { 4349caec0aeSclaudio { NOTOKEN, "", NONE, NULL}, 435bddeed9cSclaudio { COMMUNITY, "community", NONE, t_set}, 436bddeed9cSclaudio { EXTCOMMUNITY, "ext-community", NONE, t_set}, 437bddeed9cSclaudio { LRGCOMMUNITY, "large-community", NONE, t_set}, 4387ddd74cdSclaudio { LOCALPREF, "localpref", NONE, t_set}, 4397ddd74cdSclaudio { MED, "med", NONE, t_set}, 4407ddd74cdSclaudio { MED, "metric", NONE, t_set}, 4419caec0aeSclaudio { KEYWORD, "nexthop", NONE, t_nexthop}, 4429caec0aeSclaudio { KEYWORD, "pftable", NONE, t_pftable}, 4437ddd74cdSclaudio { PREPNBR, "prepend-neighbor", NONE, t_set}, 4447ddd74cdSclaudio { PREPSELF, "prepend-self", NONE, t_set}, 445a73789d3Sclaudio { KEYWORD, "rd", NONE, t_rd}, 4467ddd74cdSclaudio { WEIGHT, "weight", NONE, t_set}, 4479caec0aeSclaudio { ENDTOKEN, "", NONE, NULL} 4489caec0aeSclaudio }; 4499caec0aeSclaudio 4509caec0aeSclaudio static const struct token t_nexthop[] = { 4519caec0aeSclaudio { NEXTHOP, "", NONE, t_set}, 4529caec0aeSclaudio { ENDTOKEN, "", NONE, NULL} 4539caec0aeSclaudio }; 4549caec0aeSclaudio 4559caec0aeSclaudio static const struct token t_pftable[] = { 4569caec0aeSclaudio { PFTABLE, "", NONE, t_set}, 4579caec0aeSclaudio { ENDTOKEN, "", NONE, NULL} 4589caec0aeSclaudio }; 4599caec0aeSclaudio 460c3319070Sclaudio static const struct token t_log[] = { 461c3319070Sclaudio { KEYWORD, "brief", LOG_BRIEF, NULL}, 4623b433cf9Sclaudio { KEYWORD, "verbose", LOG_VERBOSE, NULL}, 463c3319070Sclaudio { ENDTOKEN, "", NONE, NULL} 464c3319070Sclaudio }; 465c3319070Sclaudio 466acd8015aShenning static struct parse_result res; 467acd8015aShenning 46822c4f340Sclaudio const struct token *match_token(int, char *[], const struct token [], 46922c4f340Sclaudio int *); 470acd8015aShenning void show_valid_args(const struct token []); 471a73789d3Sclaudio 472acd8015aShenning int parse_addr(const char *, struct bgpd_addr *); 47359154960Sclaudio int parse_asnum(const char *, size_t, uint32_t *); 474a73789d3Sclaudio int parse_number(const char *, struct parse_result *, enum token_type); 475bddeed9cSclaudio void parsecommunity(struct community *c, char *s); 476bddeed9cSclaudio void parselargecommunity(struct community *c, char *s); 4770ca99656Sclaudio void parseextcommunity(struct community *c, const char *t, char *s); 4789caec0aeSclaudio int parse_nexthop(const char *, struct parse_result *); 479e3925e28Sclaudio int parse_flow_numop(int, char *[], struct parse_result *, enum token_type); 480acd8015aShenning 481acd8015aShenning struct parse_result * 482acd8015aShenning parse(int argc, char *argv[]) 483acd8015aShenning { 484acd8015aShenning const struct token *table = t_main; 485acd8015aShenning const struct token *match; 48622c4f340Sclaudio int used; 487acd8015aShenning 4884a99c744Sclaudio memset(&res, 0, sizeof(res)); 4895faf3abbSbenno res.rtableid = getrtable(); 49038cb626eSfgsch TAILQ_INIT(&res.set); 491acd8015aShenning 49263aa252eSclaudio while (argc >= 0) { 49322c4f340Sclaudio if ((match = match_token(argc, argv, table, &used)) == NULL) { 494acd8015aShenning fprintf(stderr, "valid commands/args:\n"); 495acd8015aShenning show_valid_args(table); 496acd8015aShenning return (NULL); 497acd8015aShenning } 498d1d41292Sclaudio if (match->type == ANYTOKEN) { 499d1d41292Sclaudio if (prevtable == NULL) 500d1d41292Sclaudio prevtable = table; 501d1d41292Sclaudio table = match->next; 502d1d41292Sclaudio continue; 503d1d41292Sclaudio } 504acd8015aShenning 50522c4f340Sclaudio argc -= used; 50622c4f340Sclaudio argv += used; 507acd8015aShenning 5084e9d3417Shenning if (match->type == NOTOKEN || match->next == NULL) 509acd8015aShenning break; 510acd8015aShenning table = match->next; 511acd8015aShenning } 512acd8015aShenning 5134e9d3417Shenning if (argc > 0) { 5144e9d3417Shenning fprintf(stderr, "superfluous argument: %s\n", argv[0]); 515acd8015aShenning return (NULL); 516acd8015aShenning } 517acd8015aShenning 518acd8015aShenning return (&res); 519acd8015aShenning } 520acd8015aShenning 521acd8015aShenning const struct token * 52222c4f340Sclaudio match_token(int argc, char *argv[], const struct token table[], int *argsused) 523acd8015aShenning { 524acd8015aShenning u_int i, match; 525acd8015aShenning const struct token *t = NULL; 5269caec0aeSclaudio struct filter_set *fs; 52722c4f340Sclaudio const char *word = argv[0]; 52884cd1f5fStedu size_t wordlen = 0; 529acd8015aShenning 53022c4f340Sclaudio *argsused = 1; 531acd8015aShenning match = 0; 53284cd1f5fStedu if (word != NULL) 53384cd1f5fStedu wordlen = strlen(word); 534acd8015aShenning for (i = 0; table[i].type != ENDTOKEN; i++) { 535acd8015aShenning switch (table[i].type) { 536acd8015aShenning case NOTOKEN: 53784cd1f5fStedu if (word == NULL || wordlen == 0) { 538acd8015aShenning match++; 539acd8015aShenning t = &table[i]; 540acd8015aShenning } 541acd8015aShenning break; 542d1d41292Sclaudio case ANYTOKEN: 543d1d41292Sclaudio /* match anything if nothing else matched before */ 544d1d41292Sclaudio if (match == 0) { 545d1d41292Sclaudio match++; 546d1d41292Sclaudio t = &table[i]; 547d1d41292Sclaudio } 548d1d41292Sclaudio break; 549acd8015aShenning case KEYWORD: 550acd8015aShenning if (word != NULL && strncmp(word, table[i].keyword, 55184cd1f5fStedu wordlen) == 0) { 552acd8015aShenning match++; 553acd8015aShenning t = &table[i]; 554acd8015aShenning if (t->value) 555acd8015aShenning res.action = t->value; 556acd8015aShenning } 557acd8015aShenning break; 558acd8015aShenning case FLAG: 559acd8015aShenning if (word != NULL && strncmp(word, table[i].keyword, 56084cd1f5fStedu wordlen) == 0) { 561acd8015aShenning match++; 562acd8015aShenning t = &table[i]; 563acd8015aShenning res.flags |= t->value; 564acd8015aShenning } 565acd8015aShenning break; 566c41f8a59Shenning case FAMILY: 567c41f8a59Shenning if (word == NULL) 568c41f8a59Shenning break; 569e6cfebe9Sclaudio if (!strcmp(word, "inet") || 570e6cfebe9Sclaudio !strcasecmp(word, "IPv4")) { 571c41f8a59Shenning match++; 572c41f8a59Shenning t = &table[i]; 573536c61dcSclaudio res.aid = AID_INET; 574c41f8a59Shenning } 575e6cfebe9Sclaudio if (!strcmp(word, "inet6") || 576e6cfebe9Sclaudio !strcasecmp(word, "IPv6")) { 577c41f8a59Shenning match++; 578c41f8a59Shenning t = &table[i]; 579536c61dcSclaudio res.aid = AID_INET6; 580c41f8a59Shenning } 581e6cfebe9Sclaudio if (!strcasecmp(word, "VPNv4")) { 582e6cfebe9Sclaudio match++; 583e6cfebe9Sclaudio t = &table[i]; 584e6cfebe9Sclaudio res.aid = AID_VPN_IPv4; 585e6cfebe9Sclaudio } 5863eb11dfcSclaudio if (!strcasecmp(word, "VPNv6")) { 5873eb11dfcSclaudio match++; 5883eb11dfcSclaudio t = &table[i]; 5893eb11dfcSclaudio res.aid = AID_VPN_IPv6; 5903eb11dfcSclaudio } 591c41f8a59Shenning break; 592acd8015aShenning case ADDRESS: 593acd8015aShenning if (parse_addr(word, &res.addr)) { 594acd8015aShenning match++; 595acd8015aShenning t = &table[i]; 596acd8015aShenning } 597acd8015aShenning break; 598f72661b1Sclaudio case PEERADDRESS: 599f72661b1Sclaudio if (parse_addr(word, &res.peeraddr)) { 600f72661b1Sclaudio match++; 601f72661b1Sclaudio t = &table[i]; 602f72661b1Sclaudio } 603f72661b1Sclaudio break; 604e3925e28Sclaudio case FLOW_SRC: 605e3925e28Sclaudio if (parse_prefix(word, wordlen, &res.flow.src, 606e3925e28Sclaudio &res.flow.srclen)) { 607e3925e28Sclaudio match++; 608e3925e28Sclaudio t = &table[i]; 609e3925e28Sclaudio if (res.aid != res.flow.src.aid) 610e3925e28Sclaudio errx(1, "wrong address family in " 611e3925e28Sclaudio "flowspec rule"); 612e3925e28Sclaudio } 613e3925e28Sclaudio break; 614e3925e28Sclaudio case FLOW_DST: 615e3925e28Sclaudio if (parse_prefix(word, wordlen, &res.flow.dst, 616e3925e28Sclaudio &res.flow.dstlen)) { 617e3925e28Sclaudio match++; 618e3925e28Sclaudio t = &table[i]; 619e3925e28Sclaudio if (res.aid != res.flow.dst.aid) 620e3925e28Sclaudio errx(1, "wrong address family in " 621e3925e28Sclaudio "flowspec rule"); 622e3925e28Sclaudio } 623e3925e28Sclaudio break; 624d50a3b9fSclaudio case PREFIX: 62559154960Sclaudio if (parse_prefix(word, wordlen, &res.addr, 62659154960Sclaudio &res.prefixlen)) { 627d50a3b9fSclaudio match++; 628d50a3b9fSclaudio t = &table[i]; 629d50a3b9fSclaudio } 630d50a3b9fSclaudio break; 6317b6c56a0Sclaudio case ASTYPE: 6327b6c56a0Sclaudio if (word != NULL && strncmp(word, table[i].keyword, 63384cd1f5fStedu wordlen) == 0) { 6347b6c56a0Sclaudio match++; 6357b6c56a0Sclaudio t = &table[i]; 6367b6c56a0Sclaudio res.as.type = t->value; 6377b6c56a0Sclaudio } 6387b6c56a0Sclaudio break; 6397b6c56a0Sclaudio case ASNUM: 6403fdbeaafSclaudio if (parse_asnum(word, wordlen, &res.as.as_min)) { 6417ab130d7Sclaudio res.as.as_max = res.as.as_min; 6427b6c56a0Sclaudio match++; 6437b6c56a0Sclaudio t = &table[i]; 6447b6c56a0Sclaudio } 6457b6c56a0Sclaudio break; 646903de74aSclaudio case GROUPDESC: 647903de74aSclaudio res.is_group = 1; 648903de74aSclaudio /* FALLTHROUGH */ 649fd71c51dShenning case PEERDESC: 65084cd1f5fStedu if (!match && word != NULL && wordlen > 0) { 651fd71c51dShenning if (strlcpy(res.peerdesc, word, 652fd71c51dShenning sizeof(res.peerdesc)) >= 653fd71c51dShenning sizeof(res.peerdesc)) 654f186116dSclaudio errx(1, "neighbor description too " 655f186116dSclaudio "long"); 656fd71c51dShenning match++; 657fd71c51dShenning t = &table[i]; 658fd71c51dShenning } 659fd71c51dShenning break; 660a9a0337fSclaudio case RIBNAME: 66184cd1f5fStedu if (!match && word != NULL && wordlen > 0) { 662a9a0337fSclaudio if (strlcpy(res.rib, word, sizeof(res.rib)) >= 663a9a0337fSclaudio sizeof(res.rib)) 664a9a0337fSclaudio errx(1, "rib name too long"); 665a9a0337fSclaudio match++; 666a9a0337fSclaudio t = &table[i]; 667a9a0337fSclaudio } 668a9a0337fSclaudio break; 669a78f83ceSderaadt case COMMUNICATION: 6700561b344Sphessler if (!match && word != NULL && wordlen > 0) { 671a78f83ceSderaadt if (strlcpy(res.reason, word, 672a78f83ceSderaadt sizeof(res.reason)) >= 673a78f83ceSderaadt sizeof(res.reason)) 6740561b344Sphessler errx(1, "shutdown reason too long"); 6750561b344Sphessler match++; 6760561b344Sphessler t = &table[i]; 6770561b344Sphessler } 6780561b344Sphessler break; 6799caec0aeSclaudio case COMMUNITY: 680bddeed9cSclaudio if (word != NULL && strncmp(word, table[i].keyword, 68122c4f340Sclaudio wordlen) == 0 && argc > 1) { 68222c4f340Sclaudio parsecommunity(&res.community, argv[1]); 68322c4f340Sclaudio *argsused += 1; 684a73789d3Sclaudio 685a73789d3Sclaudio if ((fs = calloc(1, sizeof(*fs))) == NULL) 686a73789d3Sclaudio err(1, NULL); 687a73789d3Sclaudio fs->type = ACTION_SET_COMMUNITY; 688a73789d3Sclaudio fs->action.community = res.community; 689a73789d3Sclaudio TAILQ_INSERT_TAIL(&res.set, fs, entry); 690a73789d3Sclaudio 6919caec0aeSclaudio match++; 6929caec0aeSclaudio t = &table[i]; 6939caec0aeSclaudio } 6949caec0aeSclaudio break; 695bddeed9cSclaudio case LRGCOMMUNITY: 696e3bd233eSbenno if (word != NULL && strncmp(word, table[i].keyword, 69722c4f340Sclaudio wordlen) == 0 && argc > 1) { 69822c4f340Sclaudio parselargecommunity(&res.community, argv[1]); 69922c4f340Sclaudio *argsused += 1; 700bddeed9cSclaudio 701bddeed9cSclaudio if ((fs = calloc(1, sizeof(*fs))) == NULL) 702bddeed9cSclaudio err(1, NULL); 703bddeed9cSclaudio fs->type = ACTION_SET_COMMUNITY; 704bddeed9cSclaudio fs->action.community = res.community; 705bddeed9cSclaudio TAILQ_INSERT_TAIL(&res.set, fs, entry); 706bddeed9cSclaudio 707e3bd233eSbenno match++; 708e3bd233eSbenno t = &table[i]; 709e3bd233eSbenno } 710e3bd233eSbenno break; 711e3bd233eSbenno case EXTCOMMUNITY: 712bddeed9cSclaudio if (word != NULL && strncmp(word, table[i].keyword, 71322c4f340Sclaudio wordlen) == 0 && argc > 2) { 714a73789d3Sclaudio parseextcommunity(&res.community, 71522c4f340Sclaudio argv[1], argv[2]); 71622c4f340Sclaudio *argsused += 2; 717a73789d3Sclaudio 718a73789d3Sclaudio if ((fs = calloc(1, sizeof(*fs))) == NULL) 719a73789d3Sclaudio err(1, NULL); 720a73789d3Sclaudio fs->type = ACTION_SET_COMMUNITY; 721a73789d3Sclaudio fs->action.community = res.community; 722a73789d3Sclaudio TAILQ_INSERT_TAIL(&res.set, fs, entry); 723a73789d3Sclaudio 724e3bd233eSbenno match++; 725e3bd233eSbenno t = &table[i]; 726e3bd233eSbenno } 727e3bd233eSbenno break; 728a73789d3Sclaudio case RD: 729a73789d3Sclaudio if (word != NULL && wordlen > 0) { 730a73789d3Sclaudio char *p = strdup(word); 731f320e759Smbuhl struct community ext = { 0 }; 73259154960Sclaudio uint64_t rd; 733a73789d3Sclaudio 734a73789d3Sclaudio if (p == NULL) 735a73789d3Sclaudio err(1, NULL); 736a73789d3Sclaudio parseextcommunity(&ext, "rt", p); 737a73789d3Sclaudio free(p); 738a73789d3Sclaudio 7390ca99656Sclaudio switch (ext.data3 >> 8) { 740a73789d3Sclaudio case EXT_COMMUNITY_TRANS_TWO_AS: 741a73789d3Sclaudio rd = (0ULL << 48); 74259154960Sclaudio rd |= ((uint64_t)ext.data1 & 0xffff) 7430ca99656Sclaudio << 32; 74459154960Sclaudio rd |= (uint64_t)ext.data2; 745a73789d3Sclaudio break; 746a73789d3Sclaudio case EXT_COMMUNITY_TRANS_IPV4: 747a73789d3Sclaudio rd = (1ULL << 48); 74859154960Sclaudio rd |= (uint64_t)ext.data1 << 16; 74959154960Sclaudio rd |= (uint64_t)ext.data2 & 0xffff; 750a73789d3Sclaudio break; 751a73789d3Sclaudio case EXT_COMMUNITY_TRANS_FOUR_AS: 752a73789d3Sclaudio rd = (2ULL << 48); 75359154960Sclaudio rd |= (uint64_t)ext.data1 << 16; 75459154960Sclaudio rd |= (uint64_t)ext.data2 & 0xffff; 755a73789d3Sclaudio break; 756a73789d3Sclaudio default: 757a73789d3Sclaudio errx(1, "bad encoding of rd"); 758a73789d3Sclaudio } 759a73789d3Sclaudio res.rd = htobe64(rd); 760280f24a4Sphessler match++; 761280f24a4Sphessler t = &table[i]; 762280f24a4Sphessler } 763280f24a4Sphessler break; 7649caec0aeSclaudio case LOCALPREF: 7659caec0aeSclaudio case MED: 7669caec0aeSclaudio case PREPNBR: 7679caec0aeSclaudio case PREPSELF: 7689caec0aeSclaudio case WEIGHT: 7697f410e5eSclaudio case RTABLE: 77046daf8ccSclaudio case PATHID: 7717ddd74cdSclaudio if (word != NULL && strncmp(word, table[i].keyword, 7727ddd74cdSclaudio wordlen) == 0 && argc > 1 && 7737ddd74cdSclaudio parse_number(argv[1], &res, table[i].type)) { 7747ddd74cdSclaudio *argsused += 1; 7759caec0aeSclaudio match++; 7769caec0aeSclaudio t = &table[i]; 7779caec0aeSclaudio } 7789caec0aeSclaudio break; 7799caec0aeSclaudio case NEXTHOP: 78084cd1f5fStedu if (word != NULL && wordlen > 0 && 7819caec0aeSclaudio parse_nexthop(word, &res)) { 7829caec0aeSclaudio match++; 7839caec0aeSclaudio t = &table[i]; 7849caec0aeSclaudio } 7859caec0aeSclaudio break; 7869caec0aeSclaudio case PFTABLE: 78784cd1f5fStedu if (word != NULL && wordlen > 0) { 7889caec0aeSclaudio if ((fs = calloc(1, 7899caec0aeSclaudio sizeof(struct filter_set))) == NULL) 7909caec0aeSclaudio err(1, NULL); 7919caec0aeSclaudio if (strlcpy(fs->action.pftable, word, 7929caec0aeSclaudio sizeof(fs->action.pftable)) >= 793f186116dSclaudio sizeof(fs->action.pftable)) 794f186116dSclaudio errx(1, "pftable name too long"); 79538cb626eSfgsch TAILQ_INSERT_TAIL(&res.set, fs, entry); 7969caec0aeSclaudio match++; 7979caec0aeSclaudio t = &table[i]; 7989caec0aeSclaudio } 7999caec0aeSclaudio break; 800a20554fdSclaudio case FILENAME: 80184cd1f5fStedu if (word != NULL && wordlen > 0) { 802a20554fdSclaudio if ((res.mrtfd = open(word, O_RDONLY)) == -1) { 803a20554fdSclaudio /* 804a20554fdSclaudio * ignore error if path has no / and 805a20554fdSclaudio * does not exist. In hope to print 806a20554fdSclaudio * usage. 807a20554fdSclaudio */ 808a20554fdSclaudio if (errno == ENOENT && 809a20554fdSclaudio !strchr(word, '/')) 810a20554fdSclaudio break; 811a20554fdSclaudio err(1, "mrt open(%s)", word); 812a20554fdSclaudio } 813a20554fdSclaudio match++; 814a20554fdSclaudio t = &table[i]; 815a20554fdSclaudio } 816a20554fdSclaudio break; 817e3925e28Sclaudio case FLOW_SRCPORT: 818e3925e28Sclaudio case FLOW_DSTPORT: 819e3925e28Sclaudio case FLOW_PROTO: 820e3925e28Sclaudio case FLOW_ICMPTYPE: 821e3925e28Sclaudio case FLOW_ICMPCODE: 822e3925e28Sclaudio case FLOW_LENGTH: 823e3925e28Sclaudio case FLOW_DSCP: 824e3925e28Sclaudio if (word != NULL && strncmp(word, table[i].keyword, 825e3925e28Sclaudio wordlen) == 0 && argc > 1) { 826e3925e28Sclaudio *argsused += parse_flow_numop(argc, argv, &res, 827e3925e28Sclaudio table[i].type); 828e3925e28Sclaudio 829e3925e28Sclaudio match++; 830e3925e28Sclaudio t = &table[i]; 831e3925e28Sclaudio } 832e3925e28Sclaudio break; 833e3925e28Sclaudio case FLOW_FLAGS: 834e3925e28Sclaudio case FLOW_FRAGS: 835e3925e28Sclaudio if (word != NULL && strncmp(word, table[i].keyword, 836e3925e28Sclaudio wordlen) == 0) { 837e3925e28Sclaudio errx(1, "%s not yet implemented", word); 838e3925e28Sclaudio } 839e3925e28Sclaudio break; 840acd8015aShenning case ENDTOKEN: 841acd8015aShenning break; 842acd8015aShenning } 843acd8015aShenning } 844acd8015aShenning 845acd8015aShenning if (match != 1) { 84663aa252eSclaudio if (word == NULL) 84763aa252eSclaudio fprintf(stderr, "missing argument:\n"); 84863aa252eSclaudio else if (match > 1) 849acd8015aShenning fprintf(stderr, "ambiguous argument: %s\n", word); 85063aa252eSclaudio else if (match < 1) 851acd8015aShenning fprintf(stderr, "unknown argument: %s\n", word); 852acd8015aShenning return (NULL); 853acd8015aShenning } 854acd8015aShenning 855acd8015aShenning return (t); 856acd8015aShenning } 857acd8015aShenning 858acd8015aShenning void 859acd8015aShenning show_valid_args(const struct token table[]) 860acd8015aShenning { 861acd8015aShenning int i; 862acd8015aShenning 863d1d41292Sclaudio if (prevtable != NULL) { 864d1d41292Sclaudio const struct token *t = prevtable; 865d1d41292Sclaudio prevtable = NULL; 866d1d41292Sclaudio show_valid_args(t); 867d1d41292Sclaudio fprintf(stderr, "or any of\n"); 868d1d41292Sclaudio } 869d1d41292Sclaudio 870acd8015aShenning for (i = 0; table[i].type != ENDTOKEN; i++) { 871acd8015aShenning switch (table[i].type) { 8727b6c56a0Sclaudio case NOTOKEN: 873f45db661Shenning fprintf(stderr, " <cr>\n"); 874acd8015aShenning break; 875d1d41292Sclaudio case ANYTOKEN: 876d1d41292Sclaudio break; 877acd8015aShenning case KEYWORD: 878acd8015aShenning case FLAG: 8797b6c56a0Sclaudio case ASTYPE: 880acd8015aShenning fprintf(stderr, " %s\n", table[i].keyword); 881acd8015aShenning break; 882acd8015aShenning case ADDRESS: 883f72661b1Sclaudio case PEERADDRESS: 884acd8015aShenning fprintf(stderr, " <address>\n"); 885acd8015aShenning break; 886d50a3b9fSclaudio case PREFIX: 887e3925e28Sclaudio case FLOW_SRC: 888e3925e28Sclaudio case FLOW_DST: 889d50a3b9fSclaudio fprintf(stderr, " <address>[/<len>]\n"); 890d50a3b9fSclaudio break; 8917b6c56a0Sclaudio case ASNUM: 8927b6c56a0Sclaudio fprintf(stderr, " <asnum>\n"); 8937b6c56a0Sclaudio break; 894903de74aSclaudio case GROUPDESC: 895fd71c51dShenning case PEERDESC: 896fd71c51dShenning fprintf(stderr, " <neighbor description>\n"); 897fd71c51dShenning break; 898a9a0337fSclaudio case RIBNAME: 899a9a0337fSclaudio fprintf(stderr, " <rib name>\n"); 900a9a0337fSclaudio break; 901a78f83ceSderaadt case COMMUNICATION: 902a78f83ceSderaadt fprintf(stderr, " <reason>\n"); 9030561b344Sphessler break; 9049caec0aeSclaudio case COMMUNITY: 905bddeed9cSclaudio fprintf(stderr, " %s <community>\n", 906bddeed9cSclaudio table[i].keyword); 9079caec0aeSclaudio break; 908bddeed9cSclaudio case LRGCOMMUNITY: 909bddeed9cSclaudio fprintf(stderr, " %s <large-community>\n", 910bddeed9cSclaudio table[i].keyword); 911a73789d3Sclaudio break; 912e3bd233eSbenno case EXTCOMMUNITY: 913bddeed9cSclaudio fprintf(stderr, " %s <extended-community>\n", 914bddeed9cSclaudio table[i].keyword); 915e3bd233eSbenno break; 916a73789d3Sclaudio case RD: 917a73789d3Sclaudio fprintf(stderr, " <route-distinguisher>\n"); 918280f24a4Sphessler break; 9199caec0aeSclaudio case LOCALPREF: 9209caec0aeSclaudio case MED: 9219caec0aeSclaudio case PREPNBR: 9229caec0aeSclaudio case PREPSELF: 9239caec0aeSclaudio case WEIGHT: 9247f410e5eSclaudio case RTABLE: 9257ddd74cdSclaudio case PATHID: 9267ddd74cdSclaudio fprintf(stderr, " %s <number>\n", table[i].keyword); 9277f410e5eSclaudio break; 9289caec0aeSclaudio case NEXTHOP: 9299caec0aeSclaudio fprintf(stderr, " <address>\n"); 9309caec0aeSclaudio break; 9319caec0aeSclaudio case PFTABLE: 9329caec0aeSclaudio fprintf(stderr, " <pftable>\n"); 9339caec0aeSclaudio break; 934c41f8a59Shenning case FAMILY: 9353eb11dfcSclaudio fprintf(stderr, " [ inet | inet6 | IPv4 | IPv6 | " 9363eb11dfcSclaudio "VPNv4 | VPNv6 ]\n"); 9377f0943bdSderaadt break; 938a20554fdSclaudio case FILENAME: 939a20554fdSclaudio fprintf(stderr, " <filename>\n"); 940a20554fdSclaudio break; 941e3925e28Sclaudio case FLOW_SRCPORT: 942e3925e28Sclaudio case FLOW_DSTPORT: 943e3925e28Sclaudio case FLOW_PROTO: 944e3925e28Sclaudio case FLOW_ICMPTYPE: 945e3925e28Sclaudio case FLOW_ICMPCODE: 946e3925e28Sclaudio case FLOW_LENGTH: 947e3925e28Sclaudio case FLOW_DSCP: 948e3925e28Sclaudio fprintf(stderr, " %s <numberspec>\n", 949e3925e28Sclaudio table[i].keyword); 950e3925e28Sclaudio break; 951e3925e28Sclaudio case FLOW_FLAGS: 952e3925e28Sclaudio case FLOW_FRAGS: 953e3925e28Sclaudio fprintf(stderr, " %s <flagspec>\n", 954e3925e28Sclaudio table[i].keyword); 955e3925e28Sclaudio break; 956acd8015aShenning case ENDTOKEN: 957acd8015aShenning break; 958acd8015aShenning } 959acd8015aShenning } 960acd8015aShenning } 961acd8015aShenning 962acd8015aShenning int 963acd8015aShenning parse_addr(const char *word, struct bgpd_addr *addr) 964acd8015aShenning { 965acd8015aShenning struct in_addr ina; 966fc210fe3Sclaudio struct addrinfo hints, *r; 967acd8015aShenning 968acd8015aShenning if (word == NULL) 969acd8015aShenning return (0); 970acd8015aShenning 9714a99c744Sclaudio memset(&ina, 0, sizeof(ina)); 972acd8015aShenning 973adf6c067Sclaudio if (inet_net_pton(AF_INET, word, &ina, sizeof(ina)) != -1) { 97404fd5bf5Sclaudio memset(addr, 0, sizeof(*addr)); 975b26dd4adSclaudio addr->aid = AID_INET; 976acd8015aShenning addr->v4 = ina; 977acd8015aShenning return (1); 978acd8015aShenning } 979acd8015aShenning 9804a99c744Sclaudio memset(&hints, 0, sizeof(hints)); 981fc210fe3Sclaudio hints.ai_family = AF_INET6; 982fc210fe3Sclaudio hints.ai_socktype = SOCK_DGRAM; /*dummy*/ 983fc210fe3Sclaudio hints.ai_flags = AI_NUMERICHOST; 984fc210fe3Sclaudio if (getaddrinfo(word, "0", &hints, &r) == 0) { 985a6311673Sclaudio sa2addr(r->ai_addr, addr, NULL); 986fc210fe3Sclaudio freeaddrinfo(r); 987fc210fe3Sclaudio return (1); 988fc210fe3Sclaudio } 989fc210fe3Sclaudio 990acd8015aShenning return (0); 991acd8015aShenning } 9927b6c56a0Sclaudio 9937b6c56a0Sclaudio int 99459154960Sclaudio parse_prefix(const char *word, size_t wordlen, struct bgpd_addr *addr, 99559154960Sclaudio uint8_t *prefixlen) 996d50a3b9fSclaudio { 99704fd5bf5Sclaudio struct bgpd_addr tmp; 998fc210fe3Sclaudio char *p, *ps; 999fc210fe3Sclaudio const char *errstr; 1000fc210fe3Sclaudio int mask = -1; 1001d50a3b9fSclaudio 1002d50a3b9fSclaudio if (word == NULL) 1003d50a3b9fSclaudio return (0); 1004d50a3b9fSclaudio 100504fd5bf5Sclaudio memset(&tmp, 0, sizeof(tmp)); 1006d50a3b9fSclaudio 1007fc210fe3Sclaudio if ((p = strrchr(word, '/')) != NULL) { 100884cd1f5fStedu size_t plen = strlen(p); 1009fc210fe3Sclaudio mask = strtonum(p + 1, 0, 128, &errstr); 1010fc210fe3Sclaudio if (errstr) 1011a20554fdSclaudio errx(1, "netmask %s", errstr); 1012fc210fe3Sclaudio 101384cd1f5fStedu if ((ps = malloc(wordlen - plen + 1)) == NULL) 10143323994cSclaudio err(1, "parse_prefix: malloc"); 101584cd1f5fStedu strlcpy(ps, word, wordlen - plen + 1); 1016fc210fe3Sclaudio 101704fd5bf5Sclaudio if (parse_addr(ps, &tmp) == 0) { 101817aad25cSclaudio free(ps); 1019d50a3b9fSclaudio return (0); 102017aad25cSclaudio } 1021fc210fe3Sclaudio 1022fc210fe3Sclaudio free(ps); 1023fc210fe3Sclaudio } else 102404fd5bf5Sclaudio if (parse_addr(word, &tmp) == 0) 1025fc210fe3Sclaudio return (0); 1026fc210fe3Sclaudio 102704fd5bf5Sclaudio switch (tmp.aid) { 1028b26dd4adSclaudio case AID_INET: 1029fc210fe3Sclaudio if (mask == -1) 1030fc210fe3Sclaudio mask = 32; 1031fc210fe3Sclaudio if (mask > 32) 1032fc210fe3Sclaudio errx(1, "invalid netmask: too large"); 1033fc210fe3Sclaudio break; 1034b26dd4adSclaudio case AID_INET6: 1035fc210fe3Sclaudio if (mask == -1) 1036fc210fe3Sclaudio mask = 128; 1037fc210fe3Sclaudio break; 1038fc210fe3Sclaudio default: 1039fc210fe3Sclaudio return (0); 1040d50a3b9fSclaudio } 1041d50a3b9fSclaudio 104204fd5bf5Sclaudio applymask(addr, &tmp, mask); 1043fc210fe3Sclaudio *prefixlen = mask; 1044fc210fe3Sclaudio return (1); 1045d50a3b9fSclaudio } 1046d50a3b9fSclaudio 1047d50a3b9fSclaudio int 104859154960Sclaudio parse_asnum(const char *word, size_t wordlen, uint32_t *asnum) 10497b6c56a0Sclaudio { 1050d04c8312Sclaudio const char *errstr; 1051066ab39dSderaadt char *dot, *parseword; 105259154960Sclaudio uint32_t uval, uvalh = 0; 10537b6c56a0Sclaudio 10547b6c56a0Sclaudio if (word == NULL) 10557b6c56a0Sclaudio return (0); 10567b6c56a0Sclaudio 105784cd1f5fStedu if (wordlen < 1 || word[0] < '0' || word[0] > '9') 105870b1a84dShenning return (0); 105970b1a84dShenning 1060066ab39dSderaadt parseword = strdup(word); 1061066ab39dSderaadt if ((dot = strchr(parseword, '.')) != NULL) { 1062d18b99bcSclaudio *dot++ = '\0'; 1063066ab39dSderaadt uvalh = strtonum(parseword, 0, USHRT_MAX, &errstr); 1064d18b99bcSclaudio if (errstr) 1065d18b99bcSclaudio errx(1, "AS number is %s: %s", errstr, word); 1066d18b99bcSclaudio uval = strtonum(dot, 0, USHRT_MAX, &errstr); 1067d18b99bcSclaudio if (errstr) 1068d18b99bcSclaudio errx(1, "AS number is %s: %s", errstr, word); 1069d18b99bcSclaudio } else { 1070066ab39dSderaadt uval = strtonum(parseword, 0, UINT_MAX, &errstr); 1071d04c8312Sclaudio if (errstr) 1072d04c8312Sclaudio errx(1, "AS number is %s: %s", errstr, word); 1073d18b99bcSclaudio } 1074d04c8312Sclaudio 1075066ab39dSderaadt free(parseword); 1076d18b99bcSclaudio *asnum = uval | (uvalh << 16); 10777b6c56a0Sclaudio return (1); 10787b6c56a0Sclaudio } 10799caec0aeSclaudio 10809caec0aeSclaudio int 10819caec0aeSclaudio parse_number(const char *word, struct parse_result *r, enum token_type type) 10829caec0aeSclaudio { 10839caec0aeSclaudio struct filter_set *fs; 1084d04c8312Sclaudio const char *errstr; 1085d04c8312Sclaudio u_int uval; 10869caec0aeSclaudio 10879caec0aeSclaudio if (word == NULL) 10889caec0aeSclaudio return (0); 10899caec0aeSclaudio 1090d04c8312Sclaudio uval = strtonum(word, 0, UINT_MAX, &errstr); 1091d04c8312Sclaudio if (errstr) 1092d04c8312Sclaudio errx(1, "number is %s: %s", errstr, word); 10939caec0aeSclaudio 10949caec0aeSclaudio /* number was parseable */ 109546daf8ccSclaudio switch (type) { 109646daf8ccSclaudio case RTABLE: 10977f410e5eSclaudio r->rtableid = uval; 10987f410e5eSclaudio return (1); 109946daf8ccSclaudio case PATHID: 110046daf8ccSclaudio r->pathid = uval; 110146daf8ccSclaudio r->flags |= F_CTL_HAS_PATHID; 110246daf8ccSclaudio return (1); 110346daf8ccSclaudio default: 110446daf8ccSclaudio break; 11057f410e5eSclaudio } 11067f410e5eSclaudio 11079caec0aeSclaudio if ((fs = calloc(1, sizeof(struct filter_set))) == NULL) 11089caec0aeSclaudio err(1, NULL); 11099caec0aeSclaudio switch (type) { 11109caec0aeSclaudio case LOCALPREF: 11119caec0aeSclaudio fs->type = ACTION_SET_LOCALPREF; 1112d04c8312Sclaudio fs->action.metric = uval; 11139caec0aeSclaudio break; 11149caec0aeSclaudio case MED: 11159caec0aeSclaudio fs->type = ACTION_SET_MED; 1116d04c8312Sclaudio fs->action.metric = uval; 11179caec0aeSclaudio break; 11189caec0aeSclaudio case PREPNBR: 1119d04c8312Sclaudio if (uval > 128) { 11209caec0aeSclaudio free(fs); 11219caec0aeSclaudio return (0); 11229caec0aeSclaudio } 11239caec0aeSclaudio fs->type = ACTION_SET_PREPEND_PEER; 1124d04c8312Sclaudio fs->action.prepend = uval; 11259caec0aeSclaudio break; 11269caec0aeSclaudio case PREPSELF: 1127d04c8312Sclaudio if (uval > 128) { 11289caec0aeSclaudio free(fs); 11299caec0aeSclaudio return (0); 11309caec0aeSclaudio } 11319caec0aeSclaudio fs->type = ACTION_SET_PREPEND_SELF; 1132d04c8312Sclaudio fs->action.prepend = uval; 11339caec0aeSclaudio break; 11349caec0aeSclaudio case WEIGHT: 11359caec0aeSclaudio fs->type = ACTION_SET_WEIGHT; 1136d04c8312Sclaudio fs->action.metric = uval; 11379caec0aeSclaudio break; 11389caec0aeSclaudio default: 11399caec0aeSclaudio errx(1, "king bula sez bad things happen"); 11409caec0aeSclaudio } 11419caec0aeSclaudio 114238cb626eSfgsch TAILQ_INSERT_TAIL(&r->set, fs, entry); 11439caec0aeSclaudio return (1); 11449caec0aeSclaudio } 11459caec0aeSclaudio 1146a73789d3Sclaudio static void 114759154960Sclaudio getcommunity(char *s, int large, uint32_t *val, uint32_t *flag) 11489caec0aeSclaudio { 11498b0355c9Sclaudio long long max = USHRT_MAX; 1150d04c8312Sclaudio const char *errstr; 11519caec0aeSclaudio 1152187619b8Sclaudio *flag = 0; 1153a73789d3Sclaudio *val = 0; 1154a73789d3Sclaudio if (strcmp(s, "*") == 0) { 1155a73789d3Sclaudio *flag = COMMUNITY_ANY; 1156a73789d3Sclaudio return; 1157a73789d3Sclaudio } else if (strcmp(s, "neighbor-as") == 0) { 1158a73789d3Sclaudio *flag = COMMUNITY_NEIGHBOR_AS; 1159a73789d3Sclaudio return; 1160a73789d3Sclaudio } else if (strcmp(s, "local-as") == 0) { 1161a73789d3Sclaudio *flag = COMMUNITY_LOCAL_AS; 1162a73789d3Sclaudio return; 1163a73789d3Sclaudio } 1164a73789d3Sclaudio if (large) 1165a73789d3Sclaudio max = UINT_MAX; 1166a73789d3Sclaudio *val = strtonum(s, 0, max, &errstr); 1167a73789d3Sclaudio if (errstr) 1168a73789d3Sclaudio errx(1, "Community %s is %s (max: %llu)", s, errstr, max); 11699caec0aeSclaudio } 11709caec0aeSclaudio 1171a73789d3Sclaudio static void 117259154960Sclaudio setcommunity(struct community *c, uint32_t as, uint32_t data, 117359154960Sclaudio uint32_t asflag, uint32_t dataflag) 11749caec0aeSclaudio { 11750ca99656Sclaudio c->flags = COMMUNITY_TYPE_BASIC; 11760ca99656Sclaudio c->flags |= asflag << 8; 11770ca99656Sclaudio c->flags |= dataflag << 16; 11780ca99656Sclaudio c->data1 = as; 11790ca99656Sclaudio c->data2 = data; 11800ca99656Sclaudio c->data3 = 0; 11819caec0aeSclaudio } 11829caec0aeSclaudio 1183a73789d3Sclaudio void 1184bddeed9cSclaudio parsecommunity(struct community *c, char *s) 1185a73789d3Sclaudio { 1186a73789d3Sclaudio char *p; 118759154960Sclaudio uint32_t as, data, asflag, dataflag; 1188a73789d3Sclaudio 1189a73789d3Sclaudio /* Well-known communities */ 1190a73789d3Sclaudio if (strcasecmp(s, "GRACEFUL_SHUTDOWN") == 0) { 1191a73789d3Sclaudio setcommunity(c, COMMUNITY_WELLKNOWN, 1192a73789d3Sclaudio COMMUNITY_GRACEFUL_SHUTDOWN, 0, 0); 1193a73789d3Sclaudio return; 1194a73789d3Sclaudio } else if (strcasecmp(s, "NO_EXPORT") == 0) { 1195a73789d3Sclaudio setcommunity(c, COMMUNITY_WELLKNOWN, 1196a73789d3Sclaudio COMMUNITY_NO_EXPORT, 0, 0); 1197a73789d3Sclaudio return; 1198a73789d3Sclaudio } else if (strcasecmp(s, "NO_ADVERTISE") == 0) { 1199a73789d3Sclaudio setcommunity(c, COMMUNITY_WELLKNOWN, 1200a73789d3Sclaudio COMMUNITY_NO_ADVERTISE, 0, 0); 1201a73789d3Sclaudio return; 1202a73789d3Sclaudio } else if (strcasecmp(s, "NO_EXPORT_SUBCONFED") == 0) { 1203a73789d3Sclaudio setcommunity(c, COMMUNITY_WELLKNOWN, 1204a73789d3Sclaudio COMMUNITY_NO_EXPSUBCONFED, 0, 0); 1205a73789d3Sclaudio return; 1206a73789d3Sclaudio } else if (strcasecmp(s, "NO_PEER") == 0) { 1207a73789d3Sclaudio setcommunity(c, COMMUNITY_WELLKNOWN, 1208a73789d3Sclaudio COMMUNITY_NO_PEER, 0, 0); 1209a73789d3Sclaudio return; 1210a73789d3Sclaudio } else if (strcasecmp(s, "BLACKHOLE") == 0) { 1211a73789d3Sclaudio setcommunity(c, COMMUNITY_WELLKNOWN, 1212a73789d3Sclaudio COMMUNITY_BLACKHOLE, 0, 0); 1213a73789d3Sclaudio return; 1214a73789d3Sclaudio } 1215a73789d3Sclaudio 1216a73789d3Sclaudio if ((p = strchr(s, ':')) == NULL) 1217a73789d3Sclaudio errx(1, "Bad community syntax"); 1218a73789d3Sclaudio *p++ = 0; 1219a73789d3Sclaudio 1220a73789d3Sclaudio getcommunity(s, 0, &as, &asflag); 1221a73789d3Sclaudio getcommunity(p, 0, &data, &dataflag); 1222a73789d3Sclaudio setcommunity(c, as, data, asflag, dataflag); 1223a73789d3Sclaudio } 1224a73789d3Sclaudio 1225bddeed9cSclaudio void 1226bddeed9cSclaudio parselargecommunity(struct community *c, char *s) 1227bddeed9cSclaudio { 1228bddeed9cSclaudio char *p, *q; 1229bddeed9cSclaudio uint32_t dflag1, dflag2, dflag3; 1230bddeed9cSclaudio 1231bddeed9cSclaudio if ((p = strchr(s, ':')) == NULL) 1232bddeed9cSclaudio errx(1, "Bad community syntax"); 1233bddeed9cSclaudio *p++ = 0; 1234bddeed9cSclaudio 1235bddeed9cSclaudio if ((q = strchr(p, ':')) == NULL) 1236bddeed9cSclaudio errx(1, "Bad community syntax"); 1237bddeed9cSclaudio *q++ = 0; 1238bddeed9cSclaudio 1239bddeed9cSclaudio getcommunity(s, 1, &c->data1, &dflag1); 1240bddeed9cSclaudio getcommunity(p, 1, &c->data2, &dflag2); 1241bddeed9cSclaudio getcommunity(q, 1, &c->data3, &dflag3); 1242bddeed9cSclaudio 1243bddeed9cSclaudio c->flags = COMMUNITY_TYPE_LARGE; 1244bddeed9cSclaudio c->flags |= dflag1 << 8; 1245bddeed9cSclaudio c->flags |= dflag2 << 16; 1246bddeed9cSclaudio c->flags |= dflag3 << 24; 1247bddeed9cSclaudio } 1248bddeed9cSclaudio 1249a73789d3Sclaudio static int 1250a73789d3Sclaudio parsesubtype(const char *name, int *type, int *subtype) 1251e3bd233eSbenno { 1252e3bd233eSbenno const struct ext_comm_pairs *cp; 1253e3bd233eSbenno int found = 0; 1254e3bd233eSbenno 1255e3bd233eSbenno for (cp = iana_ext_comms; cp->subname != NULL; cp++) { 1256e3bd233eSbenno if (strcmp(name, cp->subname) == 0) { 1257e3bd233eSbenno if (found == 0) { 1258e3bd233eSbenno *type = cp->type; 1259e3bd233eSbenno *subtype = cp->subtype; 1260e3bd233eSbenno } 1261e3bd233eSbenno found++; 1262e3bd233eSbenno } 1263e3bd233eSbenno } 1264e3bd233eSbenno if (found > 1) 1265e3bd233eSbenno *type = -1; 1266e3bd233eSbenno return (found); 1267e3bd233eSbenno } 1268e3bd233eSbenno 1269a73789d3Sclaudio static int 127059154960Sclaudio parseextvalue(int type, char *s, uint32_t *v, uint32_t *flag) 1271e3bd233eSbenno { 1272e3bd233eSbenno const char *errstr; 1273e3bd233eSbenno char *p; 1274e3bd233eSbenno struct in_addr ip; 127559154960Sclaudio uint32_t uvalh, uval; 1276e3bd233eSbenno 1277a73789d3Sclaudio if (type != -1) { 1278a73789d3Sclaudio /* nothing */ 12790ca99656Sclaudio } else if (strcmp(s, "neighbor-as") == 0) { 12800ca99656Sclaudio *flag = COMMUNITY_NEIGHBOR_AS; 12810ca99656Sclaudio *v = 0; 12825aca612eSclaudio return EXT_COMMUNITY_TRANS_TWO_AS; 12830ca99656Sclaudio } else if (strcmp(s, "local-as") == 0) { 12840ca99656Sclaudio *flag = COMMUNITY_LOCAL_AS; 12850ca99656Sclaudio *v = 0; 12865aca612eSclaudio return EXT_COMMUNITY_TRANS_TWO_AS; 1287a73789d3Sclaudio } else if ((p = strchr(s, '.')) == NULL) { 1288e3bd233eSbenno /* AS_PLAIN number (4 or 2 byte) */ 1289a73789d3Sclaudio strtonum(s, 0, USHRT_MAX, &errstr); 1290a73789d3Sclaudio if (errstr == NULL) 1291a73789d3Sclaudio type = EXT_COMMUNITY_TRANS_TWO_AS; 1292e3bd233eSbenno else 1293a73789d3Sclaudio type = EXT_COMMUNITY_TRANS_FOUR_AS; 1294e3bd233eSbenno } else if (strchr(p + 1, '.') == NULL) { 1295e3bd233eSbenno /* AS_DOT number (4-byte) */ 1296a73789d3Sclaudio type = EXT_COMMUNITY_TRANS_FOUR_AS; 1297e3bd233eSbenno } else { 1298e3bd233eSbenno /* more than one dot -> IP address */ 1299a73789d3Sclaudio type = EXT_COMMUNITY_TRANS_IPV4; 1300e3bd233eSbenno } 1301e3bd233eSbenno 1302c2371130Sclaudio switch (type & EXT_COMMUNITY_VALUE) { 1303a73789d3Sclaudio case EXT_COMMUNITY_TRANS_TWO_AS: 1304a73789d3Sclaudio uval = strtonum(s, 0, USHRT_MAX, &errstr); 1305a73789d3Sclaudio if (errstr) 1306a73789d3Sclaudio errx(1, "Bad ext-community %s is %s", s, errstr); 1307a73789d3Sclaudio *v = uval; 1308a73789d3Sclaudio break; 1309a73789d3Sclaudio case EXT_COMMUNITY_TRANS_FOUR_AS: 1310a73789d3Sclaudio if ((p = strchr(s, '.')) == NULL) { 1311a73789d3Sclaudio uval = strtonum(s, 0, UINT_MAX, &errstr); 1312a73789d3Sclaudio if (errstr) 1313a73789d3Sclaudio errx(1, "Bad ext-community %s is %s", s, 1314a73789d3Sclaudio errstr); 1315a73789d3Sclaudio *v = uval; 1316a73789d3Sclaudio break; 1317a73789d3Sclaudio } 1318a73789d3Sclaudio *p++ = '\0'; 1319a73789d3Sclaudio uvalh = strtonum(s, 0, USHRT_MAX, &errstr); 1320a73789d3Sclaudio if (errstr) 1321a73789d3Sclaudio errx(1, "Bad ext-community %s is %s", s, errstr); 1322a73789d3Sclaudio uval = strtonum(p, 0, USHRT_MAX, &errstr); 1323a73789d3Sclaudio if (errstr) 1324a73789d3Sclaudio errx(1, "Bad ext-community %s is %s", p, errstr); 1325a73789d3Sclaudio *v = uval | (uvalh << 16); 1326a73789d3Sclaudio break; 1327a73789d3Sclaudio case EXT_COMMUNITY_TRANS_IPV4: 1328*f885c9d9Sflorian if (inet_pton(AF_INET, s, &ip) != 1) 1329a73789d3Sclaudio errx(1, "Bad ext-community %s not parseable", s); 1330a73789d3Sclaudio *v = ntohl(ip.s_addr); 1331a73789d3Sclaudio break; 1332a73789d3Sclaudio default: 1333a73789d3Sclaudio errx(1, "%s: unexpected type %d", __func__, type); 1334a73789d3Sclaudio } 1335a73789d3Sclaudio return (type); 1336a73789d3Sclaudio } 1337a73789d3Sclaudio 13380ca99656Sclaudio void 13390ca99656Sclaudio parseextcommunity(struct community *c, const char *t, char *s) 1340e3bd233eSbenno { 1341e3bd233eSbenno const struct ext_comm_pairs *cp; 1342e3bd233eSbenno char *p, *ep; 134359154960Sclaudio uint64_t ullval; 134459154960Sclaudio uint32_t uval, uval2, dflag1 = 0, dflag2 = 0; 13458b0355c9Sclaudio int type = 0, subtype = 0; 1346e3bd233eSbenno 13470ca99656Sclaudio if (strcmp(t, "*") == 0 && strcmp(s, "*") == 0) { 13480ca99656Sclaudio c->flags = COMMUNITY_TYPE_EXT; 13490ca99656Sclaudio c->flags |= COMMUNITY_ANY << 24; 13500ca99656Sclaudio return; 13510ca99656Sclaudio } 1352a73789d3Sclaudio if (parsesubtype(t, &type, &subtype) == 0) 1353a73789d3Sclaudio errx(1, "Bad ext-community unknown type"); 1354e3bd233eSbenno 1355e3bd233eSbenno switch (type) { 1356a73789d3Sclaudio case EXT_COMMUNITY_TRANS_TWO_AS: 1357a73789d3Sclaudio case EXT_COMMUNITY_TRANS_FOUR_AS: 1358a73789d3Sclaudio case EXT_COMMUNITY_TRANS_IPV4: 1359c2371130Sclaudio case EXT_COMMUNITY_GEN_TWO_AS: 1360c2371130Sclaudio case EXT_COMMUNITY_GEN_FOUR_AS: 1361c2371130Sclaudio case EXT_COMMUNITY_GEN_IPV4: 1362a73789d3Sclaudio case -1: 13630ca99656Sclaudio if (strcmp(s, "*") == 0) { 13640ca99656Sclaudio dflag1 = COMMUNITY_ANY; 13650ca99656Sclaudio break; 13660ca99656Sclaudio } 1367a73789d3Sclaudio if ((p = strchr(s, ':')) == NULL) 1368a73789d3Sclaudio errx(1, "Bad ext-community %s", s); 1369e3bd233eSbenno *p++ = '\0'; 13700ca99656Sclaudio type = parseextvalue(type, s, &uval, &dflag1); 13710ca99656Sclaudio 1372e3bd233eSbenno switch (type) { 1373e3bd233eSbenno case EXT_COMMUNITY_TRANS_TWO_AS: 1374c2371130Sclaudio case EXT_COMMUNITY_GEN_TWO_AS: 13750ca99656Sclaudio getcommunity(p, 1, &uval2, &dflag2); 1376e3bd233eSbenno break; 1377e3bd233eSbenno case EXT_COMMUNITY_TRANS_IPV4: 1378e3bd233eSbenno case EXT_COMMUNITY_TRANS_FOUR_AS: 1379c2371130Sclaudio case EXT_COMMUNITY_GEN_IPV4: 1380c2371130Sclaudio case EXT_COMMUNITY_GEN_FOUR_AS: 13810ca99656Sclaudio getcommunity(p, 0, &uval2, &dflag2); 1382e3bd233eSbenno break; 1383e3bd233eSbenno default: 1384a73789d3Sclaudio errx(1, "parseextcommunity: unexpected result"); 1385e3bd233eSbenno } 13860ca99656Sclaudio 13870ca99656Sclaudio c->data1 = uval; 13880ca99656Sclaudio c->data2 = uval2; 1389e3bd233eSbenno break; 1390e3bd233eSbenno case EXT_COMMUNITY_TRANS_OPAQUE: 1391e3bd233eSbenno case EXT_COMMUNITY_TRANS_EVPN: 13920ca99656Sclaudio if (strcmp(s, "*") == 0) { 13930ca99656Sclaudio dflag1 = COMMUNITY_ANY; 13940ca99656Sclaudio break; 13950ca99656Sclaudio } 1396e3bd233eSbenno errno = 0; 1397a73789d3Sclaudio ullval = strtoull(s, &ep, 0); 1398a73789d3Sclaudio if (s[0] == '\0' || *ep != '\0') 1399a73789d3Sclaudio errx(1, "Bad ext-community bad value"); 1400a73789d3Sclaudio if (errno == ERANGE && ullval > EXT_COMMUNITY_OPAQUE_MAX) 1401a73789d3Sclaudio errx(1, "Bad ext-community value too big"); 14020ca99656Sclaudio c->data1 = ullval >> 32; 14030ca99656Sclaudio c->data2 = ullval; 1404e3bd233eSbenno break; 1405e3bd233eSbenno case EXT_COMMUNITY_NON_TRANS_OPAQUE: 14060ca99656Sclaudio if (subtype == EXT_COMMUNITY_SUBTYPE_OVS) { 14070ca99656Sclaudio if (strcmp(s, "valid") == 0) { 14080ca99656Sclaudio c->data2 = EXT_COMMUNITY_OVS_VALID; 14090ca99656Sclaudio break; 14100ca99656Sclaudio } else if (strcmp(s, "invalid") == 0) { 14110ca99656Sclaudio c->data2 = EXT_COMMUNITY_OVS_INVALID; 14120ca99656Sclaudio break; 14130ca99656Sclaudio } else if (strcmp(s, "not-found") == 0) { 14140ca99656Sclaudio c->data2 = EXT_COMMUNITY_OVS_NOTFOUND; 14150ca99656Sclaudio break; 14160ca99656Sclaudio } else if (strcmp(s, "*") == 0) { 14170ca99656Sclaudio dflag1 = COMMUNITY_ANY; 1418e3bd233eSbenno break; 1419e3bd233eSbenno } 14200ca99656Sclaudio } 14210ca99656Sclaudio errx(1, "Bad ext-community %s", s); 14220ca99656Sclaudio } 14230ca99656Sclaudio 14240ca99656Sclaudio c->data3 = type << 8 | subtype; 14250ca99656Sclaudio 14260ca99656Sclaudio /* special handling of ext-community rt * since type is not known */ 14270ca99656Sclaudio if (dflag1 == COMMUNITY_ANY && type == -1) { 14280ca99656Sclaudio c->flags = COMMUNITY_TYPE_EXT; 14290ca99656Sclaudio c->flags |= dflag1 << 8; 14300ca99656Sclaudio return; 14310ca99656Sclaudio } 1432e3bd233eSbenno 1433e3bd233eSbenno /* verify type/subtype combo */ 1434e3bd233eSbenno for (cp = iana_ext_comms; cp->subname != NULL; cp++) { 1435a73789d3Sclaudio if (cp->type == type && cp->subtype == subtype) { 14360ca99656Sclaudio c->flags = COMMUNITY_TYPE_EXT; 14370ca99656Sclaudio c->flags |= dflag1 << 8; 14380ca99656Sclaudio c->flags |= dflag2 << 16; 14390ca99656Sclaudio return; 1440e3bd233eSbenno } 1441280f24a4Sphessler } 1442280f24a4Sphessler 1443a73789d3Sclaudio errx(1, "Bad ext-community bad format for type"); 1444280f24a4Sphessler } 1445280f24a4Sphessler 14469caec0aeSclaudio int 14479caec0aeSclaudio parse_nexthop(const char *word, struct parse_result *r) 14489caec0aeSclaudio { 14499caec0aeSclaudio struct filter_set *fs; 14509caec0aeSclaudio 14519caec0aeSclaudio if ((fs = calloc(1, sizeof(struct filter_set))) == NULL) 14529caec0aeSclaudio err(1, NULL); 14539caec0aeSclaudio 14549caec0aeSclaudio if (strcmp(word, "blackhole") == 0) 14559caec0aeSclaudio fs->type = ACTION_SET_NEXTHOP_BLACKHOLE; 14569caec0aeSclaudio else if (strcmp(word, "reject") == 0) 14579caec0aeSclaudio fs->type = ACTION_SET_NEXTHOP_REJECT; 14589caec0aeSclaudio else if (strcmp(word, "no-modify") == 0) 14599caec0aeSclaudio fs->type = ACTION_SET_NEXTHOP_NOMODIFY; 14609caec0aeSclaudio else if (parse_addr(word, &fs->action.nexthop)) { 14619caec0aeSclaudio fs->type = ACTION_SET_NEXTHOP; 14629caec0aeSclaudio } else { 14639caec0aeSclaudio free(fs); 14649caec0aeSclaudio return (0); 14659caec0aeSclaudio } 14669caec0aeSclaudio 146738cb626eSfgsch TAILQ_INSERT_TAIL(&r->set, fs, entry); 14689caec0aeSclaudio return (1); 14699caec0aeSclaudio } 1470e3925e28Sclaudio 1471e3925e28Sclaudio static int 1472e3925e28Sclaudio unary_op(const char *op) 1473e3925e28Sclaudio { 1474e3925e28Sclaudio if (strcmp(op, "=") == 0) 1475e3925e28Sclaudio return FLOWSPEC_OP_NUM_EQ; 1476e3925e28Sclaudio if (strcmp(op, "!=") == 0) 1477e3925e28Sclaudio return FLOWSPEC_OP_NUM_NOT; 1478e3925e28Sclaudio if (strcmp(op, ">") == 0) 1479e3925e28Sclaudio return FLOWSPEC_OP_NUM_GT; 1480e3925e28Sclaudio if (strcmp(op, ">=") == 0) 1481e3925e28Sclaudio return FLOWSPEC_OP_NUM_GE; 1482e3925e28Sclaudio if (strcmp(op, "<") == 0) 1483e3925e28Sclaudio return FLOWSPEC_OP_NUM_LT; 1484e3925e28Sclaudio if (strcmp(op, "<=") == 0) 1485e3925e28Sclaudio return FLOWSPEC_OP_NUM_LE; 1486e3925e28Sclaudio return -1; 1487e3925e28Sclaudio } 1488e3925e28Sclaudio 1489e3925e28Sclaudio static enum comp_ops 1490e3925e28Sclaudio binary_op(const char *op) 1491e3925e28Sclaudio { 1492e3925e28Sclaudio if (strcmp(op, "-") == 0) 1493e3925e28Sclaudio return OP_RANGE; 1494e3925e28Sclaudio if (strcmp(op, "><") == 0) 1495e3925e28Sclaudio return OP_XRANGE; 1496e3925e28Sclaudio return OP_NONE; 1497e3925e28Sclaudio } 1498e3925e28Sclaudio 1499e3925e28Sclaudio static void 1500e3925e28Sclaudio push_numop(struct parse_result *r, int type, uint8_t op, int and, long long val) 1501e3925e28Sclaudio { 1502e3925e28Sclaudio uint8_t *comp; 1503e3925e28Sclaudio void *data; 1504e3925e28Sclaudio uint32_t u32; 1505e3925e28Sclaudio uint16_t u16; 1506e3925e28Sclaudio uint8_t u8, flag = 0; 1507e3925e28Sclaudio int len, complen; 1508e3925e28Sclaudio 1509e3925e28Sclaudio flag |= op; 1510e3925e28Sclaudio if (and) 1511e3925e28Sclaudio flag |= FLOWSPEC_OP_AND; 1512e3925e28Sclaudio 1513e3925e28Sclaudio if (val < 0 || val > 0xffffffff) { 1514e3925e28Sclaudio errx(1, "unsupported value for flowspec num_op"); 1515e3925e28Sclaudio } else if (val <= 255) { 1516e3925e28Sclaudio len = 1; 1517e3925e28Sclaudio u8 = val; 1518e3925e28Sclaudio data = &u8; 1519e3925e28Sclaudio } else if (val <= 0xffff) { 1520e3925e28Sclaudio len = 2; 1521e3925e28Sclaudio u16 = htons(val); 1522e3925e28Sclaudio data = &u16; 1523e3925e28Sclaudio flag |= 1 << FLOWSPEC_OP_LEN_SHIFT; 1524e3925e28Sclaudio } else { 1525e3925e28Sclaudio len = 4; 1526e3925e28Sclaudio u32 = htonl(val); 1527e3925e28Sclaudio data = &u32; 1528e3925e28Sclaudio flag |= 2 << FLOWSPEC_OP_LEN_SHIFT; 1529e3925e28Sclaudio } 1530e3925e28Sclaudio 1531e3925e28Sclaudio complen = r->flow.complen[type]; 1532e3925e28Sclaudio comp = realloc(r->flow.components[type], complen + len + 1); 1533e3925e28Sclaudio if (comp == NULL) 1534e3925e28Sclaudio err(1, NULL); 1535e3925e28Sclaudio 1536e3925e28Sclaudio comp[complen++] = flag; 1537e3925e28Sclaudio memcpy(comp + complen, data, len); 1538e3925e28Sclaudio complen += len; 1539e3925e28Sclaudio r->flow.complen[type] = complen; 1540e3925e28Sclaudio r->flow.components[type] = comp; 1541e3925e28Sclaudio } 1542e3925e28Sclaudio 1543e3925e28Sclaudio int 1544e3925e28Sclaudio parse_flow_numop(int argc, char *argv[], struct parse_result *r, 1545e3925e28Sclaudio enum token_type toktype) 1546e3925e28Sclaudio { 1547e3925e28Sclaudio const char *errstr; 1548e3925e28Sclaudio long long val, val2; 1549e3925e28Sclaudio int numargs, type; 1550e3925e28Sclaudio int is_list = 0; 1551e3925e28Sclaudio int op; 1552e3925e28Sclaudio 1553e3925e28Sclaudio switch (toktype) { 1554e3925e28Sclaudio case FLOW_PROTO: 1555e3925e28Sclaudio type = FLOWSPEC_TYPE_PROTO; 1556e3925e28Sclaudio break; 1557e3925e28Sclaudio case FLOW_SRCPORT: 1558e3925e28Sclaudio type = FLOWSPEC_TYPE_SRC_PORT; 1559e3925e28Sclaudio break; 1560e3925e28Sclaudio case FLOW_DSTPORT: 1561e3925e28Sclaudio type = FLOWSPEC_TYPE_DST_PORT; 1562e3925e28Sclaudio break; 1563e3925e28Sclaudio case FLOW_ICMPTYPE: 1564e3925e28Sclaudio type = FLOWSPEC_TYPE_ICMP_TYPE; 1565e3925e28Sclaudio break; 1566e3925e28Sclaudio case FLOW_ICMPCODE: 1567e3925e28Sclaudio type = FLOWSPEC_TYPE_ICMP_CODE; 1568e3925e28Sclaudio break; 1569e3925e28Sclaudio case FLOW_LENGTH: 1570e3925e28Sclaudio type = FLOWSPEC_TYPE_PKT_LEN; 1571e3925e28Sclaudio break; 1572e3925e28Sclaudio case FLOW_DSCP: 1573e3925e28Sclaudio type = FLOWSPEC_TYPE_DSCP; 1574e3925e28Sclaudio break; 1575e3925e28Sclaudio default: 1576e3925e28Sclaudio errx(1, "parse_flow_numop called with unsupported type"); 1577e3925e28Sclaudio } 1578e3925e28Sclaudio 1579e3925e28Sclaudio /* skip keyword (which is already accounted for) */ 1580e3925e28Sclaudio argc--; 1581e3925e28Sclaudio argv++; 1582e3925e28Sclaudio numargs = argc; 1583e3925e28Sclaudio 1584e3925e28Sclaudio while (argc > 0) { 1585e3925e28Sclaudio if (strcmp(argv[0], "{") == 0) { 1586e3925e28Sclaudio is_list = 1; 1587e3925e28Sclaudio argc--; 1588e3925e28Sclaudio argv++; 1589e3925e28Sclaudio } else if (is_list && strcmp(argv[0], "}") == 0) { 1590e3925e28Sclaudio is_list = 0; 1591e3925e28Sclaudio argc--; 1592e3925e28Sclaudio argv++; 1593e3925e28Sclaudio } else if ((op = unary_op(argv[0])) != -1) { 1594e3925e28Sclaudio if (argc < 2) 1595e3925e28Sclaudio errx(1, "missing argument in flowspec " 1596e3925e28Sclaudio "definition"); 1597e3925e28Sclaudio 1598e3925e28Sclaudio val = strtonum(argv[1], LLONG_MIN, LLONG_MAX, &errstr); 1599e3925e28Sclaudio if (errstr) 1600e3925e28Sclaudio errx(1, "\"%s\" invalid number: %s", argv[0], 1601e3925e28Sclaudio errstr); 1602e3925e28Sclaudio push_numop(r, type, op, 0, val); 1603e3925e28Sclaudio argc -= 2; 1604e3925e28Sclaudio argv += 2; 1605e3925e28Sclaudio } else { 1606e3925e28Sclaudio val = strtonum(argv[0], LLONG_MIN, LLONG_MAX, &errstr); 1607e3925e28Sclaudio if (errstr) 1608e3925e28Sclaudio errx(1, "\"%s\" invalid number: %s", argv[0], 1609e3925e28Sclaudio errstr); 1610e3925e28Sclaudio if (argc >= 3 && (op = binary_op(argv[1])) != OP_NONE) { 1611e3925e28Sclaudio val2 = strtonum(argv[2], LLONG_MIN, LLONG_MAX, 1612e3925e28Sclaudio &errstr); 1613e3925e28Sclaudio if (errstr) 1614e3925e28Sclaudio errx(1, "\"%s\" invalid number: %s", 1615e3925e28Sclaudio argv[2], errstr); 1616e3925e28Sclaudio switch (op) { 1617e3925e28Sclaudio case OP_RANGE: 1618e3925e28Sclaudio push_numop(r, type, FLOWSPEC_OP_NUM_GE, 1619e3925e28Sclaudio 0, val); 1620e3925e28Sclaudio push_numop(r, type, FLOWSPEC_OP_NUM_LE, 1621e3925e28Sclaudio 1, val2); 1622e3925e28Sclaudio break; 1623e3925e28Sclaudio case OP_XRANGE: 1624e3925e28Sclaudio push_numop(r, type, FLOWSPEC_OP_NUM_LT, 1625e3925e28Sclaudio 0, val); 1626e3925e28Sclaudio push_numop(r, type, FLOWSPEC_OP_NUM_GT, 1627e3925e28Sclaudio 0, val2); 1628e3925e28Sclaudio break; 1629e3925e28Sclaudio } 1630e3925e28Sclaudio argc -= 3; 1631e3925e28Sclaudio argv += 3; 1632e3925e28Sclaudio } else { 1633e3925e28Sclaudio push_numop(r, type, FLOWSPEC_OP_NUM_EQ, 0, val); 1634e3925e28Sclaudio argc--; 1635e3925e28Sclaudio argv++; 1636e3925e28Sclaudio } 1637e3925e28Sclaudio } 1638e3925e28Sclaudio if (is_list == 0) 1639e3925e28Sclaudio break; 1640e3925e28Sclaudio } 1641e3925e28Sclaudio 1642e3925e28Sclaudio return numargs - argc; 1643e3925e28Sclaudio } 1644