1 %{ 2 /* 3 * Copyright (C) 2001-2008 by Darren Reed. 4 * 5 * See the IPFILTER.LICENCE file for details on licencing. 6 * 7 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 8 * Use is subject to license terms. 9 */ 10 11 #pragma ident "%Z%%M% %I% %E% SMI" 12 13 #ifdef __FreeBSD__ 14 # ifndef __FreeBSD_cc_version 15 # include <osreldate.h> 16 # else 17 # if __FreeBSD_cc_version < 430000 18 # include <osreldate.h> 19 # endif 20 # endif 21 #endif 22 #include <stdio.h> 23 #include <unistd.h> 24 #include <string.h> 25 #include <fcntl.h> 26 #include <errno.h> 27 #if !defined(__SVR4) && !defined(__GNUC__) 28 #include <strings.h> 29 #endif 30 #include <sys/types.h> 31 #include <sys/param.h> 32 #include <sys/file.h> 33 #include <stdlib.h> 34 #include <stddef.h> 35 #include <sys/socket.h> 36 #include <sys/ioctl.h> 37 #include <netinet/in.h> 38 #include <netinet/in_systm.h> 39 #include <sys/time.h> 40 #include <syslog.h> 41 #include <net/if.h> 42 #if __FreeBSD_version >= 300000 43 # include <net/if_var.h> 44 #endif 45 #include <netdb.h> 46 #include <arpa/nameser.h> 47 #include <resolv.h> 48 #include "ipf.h" 49 #include "netinet/ipl.h" 50 #include "ipnat_l.h" 51 52 #define YYDEBUG 1 53 54 extern void yyerror __P((char *)); 55 extern int yyparse __P((void)); 56 extern int yylex __P((void)); 57 extern int yydebug; 58 extern FILE *yyin; 59 extern int yylineNum; 60 61 static ipnat_t *nattop = NULL; 62 static ipnat_t *nat = NULL; 63 static int natfd = -1; 64 static ioctlfunc_t natioctlfunc = NULL; 65 static addfunc_t nataddfunc = NULL; 66 67 static void newnatrule __P((void)); 68 static void setnatproto __P((int)); 69 70 %} 71 %union { 72 char *str; 73 u_32_t num; 74 struct { 75 i6addr_t a; 76 int v; 77 } ipa; 78 frentry_t fr; 79 frtuc_t *frt; 80 u_short port; 81 struct { 82 u_short p1; 83 u_short p2; 84 int pc; 85 } pc; 86 struct { 87 i6addr_t a; 88 i6addr_t m; 89 int v; 90 } ipp; 91 union i6addr ip6; 92 }; 93 94 %token <num> YY_NUMBER YY_HEX 95 %token <str> YY_STR 96 %token YY_COMMENT 97 %token YY_CMP_EQ YY_CMP_NE YY_CMP_LE YY_CMP_GE YY_CMP_LT YY_CMP_GT 98 %token YY_RANGE_OUT YY_RANGE_IN 99 %token <ip6> YY_IPV6 100 101 %token IPNY_MAPBLOCK IPNY_RDR IPNY_PORT IPNY_PORTS IPNY_AUTO IPNY_RANGE 102 %token IPNY_MAP IPNY_BIMAP IPNY_FROM IPNY_TO IPNY_MASK IPNY_PORTMAP IPNY_ANY 103 %token IPNY_ROUNDROBIN IPNY_FRAG IPNY_AGE IPNY_ICMPIDMAP IPNY_PROXY 104 %token IPNY_TCP IPNY_UDP IPNY_TCPUDP IPNY_STICKY IPNY_MSSCLAMP IPNY_TAG 105 %token IPNY_TLATE 106 %type <port> portspec 107 %type <num> hexnumber compare range proto 108 %type <num> saddr daddr sobject dobject mapfrom rdrfrom dip 109 %type <ipa> hostname ipv4 ipaddr 110 %type <ipp> addr rhaddr 111 %type <pc> portstuff 112 %% 113 file: line 114 | assign 115 | file line 116 | file assign 117 ; 118 119 line: xx rule { while ((nat = nattop) != NULL) { 120 if (nat->in_v == 0) 121 nat->in_v = 4; 122 nattop = nat->in_next; 123 (*nataddfunc)(natfd, natioctlfunc, nat); 124 free(nat); 125 } 126 resetlexer(); 127 } 128 | YY_COMMENT 129 ; 130 131 assign: YY_STR assigning YY_STR ';' { set_variable($1, $3); 132 resetlexer(); 133 free($1); 134 free($3); 135 } 136 ; 137 138 assigning: 139 '=' { yyvarnext = 1; } 140 ; 141 142 xx: { newnatrule(); } 143 ; 144 145 rule: map eol 146 | mapblock eol 147 | redir eol 148 ; 149 150 eol: | ';' 151 ; 152 153 map: mapit ifnames addr IPNY_TLATE rhaddr proxy mapoptions 154 { if ($3.v != 0 && $3.v != $5.v && $5.v != 0) 155 yyerror("1.address family mismatch"); 156 bcopy(&$3.a, &nat->in_in[0], sizeof($3.a)); 157 bcopy(&$3.m, &nat->in_in[1], sizeof($3.a)); 158 bcopy(&$5.a, &nat->in_out[0], sizeof($5.a)); 159 bcopy(&$5.m, &nat->in_out[1], sizeof($5.a)); 160 if (nat->in_ifnames[1][0] == '\0') 161 strncpy(nat->in_ifnames[1], 162 nat->in_ifnames[0], 163 sizeof(nat->in_ifnames[0])); 164 if ((nat->in_flags & IPN_TCPUDP) == 0) 165 setnatproto(nat->in_p); 166 if (((nat->in_redir & NAT_MAPBLK) != 0) || 167 ((nat->in_flags & IPN_AUTOPORTMAP) != 0)) 168 nat_setgroupmap(nat); 169 } 170 | mapit ifnames addr IPNY_TLATE rhaddr mapport mapoptions 171 { if ($3.v != 0 && $3.v != $5.v && $5.v != 0) 172 yyerror("2.address family mismatch"); 173 bcopy(&$3.a, &nat->in_in[0], sizeof($3.a)); 174 bcopy(&$3.m, &nat->in_in[1], sizeof($3.a)); 175 bcopy(&$5.a, &nat->in_out[0], sizeof($5.a)); 176 bcopy(&$5.m, &nat->in_out[1], sizeof($5.a)); 177 if (nat->in_ifnames[1][0] == '\0') 178 strncpy(nat->in_ifnames[1], 179 nat->in_ifnames[0], 180 sizeof(nat->in_ifnames[0])); 181 if ((nat->in_flags & IPN_TCPUDPICMPQ) == 0) 182 setnatproto(nat->in_p); 183 if (((nat->in_redir & NAT_MAPBLK) != 0) || 184 ((nat->in_flags & IPN_AUTOPORTMAP) != 0)) 185 nat_setgroupmap(nat); 186 } 187 | mapit ifnames mapfrom IPNY_TLATE rhaddr proxy mapoptions 188 { if ($3 != 0 && $3 != $5.v && $5.v != 0) 189 yyerror("3.address family mismatch"); 190 bcopy(&$5.a, &nat->in_out[0], sizeof($5.a)); 191 bcopy(&$5.m, &nat->in_out[1], sizeof($5.a)); 192 if (nat->in_ifnames[1][0] == '\0') 193 strncpy(nat->in_ifnames[1], 194 nat->in_ifnames[0], 195 sizeof(nat->in_ifnames[0])); 196 if ((nat->in_flags & IPN_TCPUDP) == 0) 197 setnatproto(nat->in_p); 198 if (((nat->in_redir & NAT_MAPBLK) != 0) || 199 ((nat->in_flags & IPN_AUTOPORTMAP) != 0)) 200 nat_setgroupmap(nat); 201 } 202 | mapit ifnames mapfrom IPNY_TLATE rhaddr mapport mapoptions 203 { if ($3 != 0 && $3 != $5.v && $5.v != 0) 204 yyerror("4.address family mismatch"); 205 bcopy(&$5.a, &nat->in_out[0], sizeof($5.a)); 206 bcopy(&$5.m, &nat->in_out[1], sizeof($5.a)); 207 if (nat->in_ifnames[1][0] == '\0') 208 strncpy(nat->in_ifnames[1], 209 nat->in_ifnames[0], 210 sizeof(nat->in_ifnames[0])); 211 if ((nat->in_flags & IPN_TCPUDPICMPQ) == 0) 212 setnatproto(nat->in_p); 213 if (((nat->in_redir & NAT_MAPBLK) != 0) || 214 ((nat->in_flags & IPN_AUTOPORTMAP) != 0)) 215 nat_setgroupmap(nat); 216 } 217 ; 218 219 mapblock: 220 mapblockit ifnames addr IPNY_TLATE addr ports mapoptions 221 { if ($3.v != 0 && $3.v != $5.v && $5.v != 0) 222 yyerror("5.address family mismatch"); 223 bcopy(&$3.a, &nat->in_in[0], sizeof($3.a)); 224 bcopy(&$3.m, &nat->in_in[1], sizeof($3.a)); 225 bcopy(&$5.a, &nat->in_out[0], sizeof($5.a)); 226 bcopy(&$5.m, &nat->in_out[1], sizeof($5.a)); 227 if (nat->in_ifnames[1][0] == '\0') 228 strncpy(nat->in_ifnames[1], 229 nat->in_ifnames[0], 230 sizeof(nat->in_ifnames[0])); 231 if ((nat->in_flags & IPN_TCPUDP) == 0) 232 setnatproto(nat->in_p); 233 if (((nat->in_redir & NAT_MAPBLK) != 0) || 234 ((nat->in_flags & IPN_AUTOPORTMAP) != 0)) 235 nat_setgroupmap(nat); 236 } 237 ; 238 239 redir: rdrit ifnames addr dport IPNY_TLATE dip nport setproto rdroptions 240 { if ($6 != 0 && $3.v != 0 && $6 != $3.v) 241 yyerror("6.address family mismatch"); 242 bcopy(&$3.a, &nat->in_out[0], sizeof($3.a)); 243 bcopy(&$3.m, &nat->in_out[1], sizeof($3.a)); 244 if (nat->in_ifnames[1][0] == '\0') 245 strncpy(nat->in_ifnames[1], 246 nat->in_ifnames[0], 247 sizeof(nat->in_ifnames[0])); 248 if ((nat->in_p == 0) && 249 ((nat->in_flags & IPN_TCPUDP) == 0) && 250 (nat->in_pmin != 0 || 251 nat->in_pmax != 0 || 252 nat->in_pnext != 0)) 253 setnatproto(IPPROTO_TCP); 254 } 255 | rdrit ifnames rdrfrom IPNY_TLATE dip nport setproto rdroptions 256 { if ($5 != 0 && $3 != 0 && $5 != $3) 257 yyerror("7.address family mismatch"); 258 if ((nat->in_p == 0) && 259 ((nat->in_flags & IPN_TCPUDP) == 0) && 260 (nat->in_pmin != 0 || 261 nat->in_pmax != 0 || 262 nat->in_pnext != 0)) 263 setnatproto(IPPROTO_TCP); 264 if (nat->in_ifnames[1][0] == '\0') 265 strncpy(nat->in_ifnames[1], 266 nat->in_ifnames[0], 267 sizeof(nat->in_ifnames[0])); 268 } 269 | rdrit ifnames addr IPNY_TLATE dip setproto rdroptions 270 { if ($5 != 0 && $3.v != 0 && $5 != $3.v) 271 yyerror("8.address family mismatch"); 272 bcopy(&$3.a, &nat->in_out[0], sizeof($3.a)); 273 bcopy(&$3.m, &nat->in_out[1], sizeof($3.a)); 274 if (nat->in_ifnames[1][0] == '\0') 275 strncpy(nat->in_ifnames[1], 276 nat->in_ifnames[0], 277 sizeof(nat->in_ifnames[0])); 278 } 279 ; 280 281 proxy: | IPNY_PROXY IPNY_PORT portspec YY_STR '/' proto 282 { strncpy(nat->in_plabel, $4, sizeof(nat->in_plabel)); 283 if (nat->in_dcmp == 0) { 284 nat->in_dport = htons($3); 285 } else if ($3 != nat->in_dport) { 286 yyerror("proxy port numbers not consistant"); 287 } 288 setnatproto($6); 289 free($4); 290 } 291 | IPNY_PROXY IPNY_PORT YY_STR YY_STR '/' proto 292 { int pnum; 293 strncpy(nat->in_plabel, $4, sizeof(nat->in_plabel)); 294 pnum = getportproto($3, $6); 295 if (pnum == -1) 296 yyerror("invalid port number"); 297 nat->in_dport = pnum; 298 setnatproto($6); 299 free($3); 300 free($4); 301 } 302 ; 303 304 setproto: 305 | proto { if (nat->in_p != 0 || 306 nat->in_flags & IPN_TCPUDP) 307 yyerror("protocol set twice"); 308 setnatproto($1); 309 } 310 | IPNY_TCPUDP { if (nat->in_p != 0 || 311 nat->in_flags & IPN_TCPUDP) 312 yyerror("protocol set twice"); 313 nat->in_flags |= IPN_TCPUDP; 314 nat->in_p = 0; 315 } 316 | IPNY_TCP '/' IPNY_UDP { if (nat->in_p != 0 || 317 nat->in_flags & IPN_TCPUDP) 318 yyerror("protocol set twice"); 319 nat->in_flags |= IPN_TCPUDP; 320 nat->in_p = 0; 321 } 322 ; 323 324 rhaddr: addr { $$.a = $1.a; 325 $$.m = $1.m; 326 $$.v = $1.v; 327 if ($$.v == 0) 328 $$.v = nat->in_v; 329 yyexpectaddr = 0; } 330 | IPNY_RANGE hostname '-' hostname 331 { if ($2.v != 0 && $4.v != 0 && $4.v != $2.v) 332 yyerror("9.address family " 333 "mismatch"); 334 $$.v = $2.v; 335 $$.a = $2.a; 336 $$.m = $4.a; 337 nat->in_flags |= IPN_IPRANGE; 338 yyexpectaddr = 0; } 339 ; 340 341 dip: 342 hostname { bcopy(&$1.a, &nat->in_in[0], 343 sizeof($1.a)); 344 if ($1.v == 0) 345 $1.v = nat->in_v; 346 if ($1.v == 4) { 347 nat->in_inmsk = 0xffffffff; 348 } else { 349 nat->in_in[1].i6[0] = 0xffffffff; 350 nat->in_in[1].i6[1] = 0xffffffff; 351 nat->in_in[1].i6[2] = 0xffffffff; 352 nat->in_in[1].i6[3] = 0xffffffff; 353 } 354 $$ = $1.v; 355 } 356 | hostname '/' YY_NUMBER { if ($1.v == 0) 357 $1.v = nat->in_v; 358 if ($1.v == 4 && 359 ($1.a.in4.s_addr != 0 || 360 ($3 != 0 && $3 != 32))) 361 yyerror("Invalid mask for dip"); 362 else if ($1.v == 6 && 363 ($1.a.in4.s_addr != 0 || 364 ($3 != 0 && $3 != 128))) 365 yyerror("Invalid mask for dip"); 366 else if ($1.v == 0 ) { 367 if ($1.a.in4.s_addr == 0 && 368 ($3 == 32 || $3 == 0)) 369 $1.v = 4; 370 else if ($3 == 128) 371 $1.v = 6; 372 } 373 bcopy(&$1.a, &nat->in_in[0], 374 sizeof($1.a)); 375 ntomask($1.v, $3, 376 (u_32_t *)&nat->in_in[1]); 377 nat->in_in[0].i6[0] &= nat->in_in[1].i6[0]; 378 nat->in_in[0].i6[0] &= nat->in_in[1].i6[1]; 379 nat->in_in[0].i6[0] &= nat->in_in[1].i6[2]; 380 nat->in_in[0].i6[0] &= nat->in_in[1].i6[3]; 381 nat->in_v = $1.v; 382 $$ = $1.v; 383 } 384 | hostname ',' { yyexpectaddr = 1; } hostname 385 { if ($1.v != $4.v) 386 yyerror("10.address family " 387 "mismatch"); 388 $$ = $1.v; 389 nat->in_flags |= IPN_SPLIT; 390 bcopy(&$1.a, &nat->in_in[0], 391 sizeof($1.a)); 392 bcopy(&$4.a, &nat->in_in[1], 393 sizeof($4.a)); 394 yyexpectaddr = 0; } 395 ; 396 397 portspec: 398 YY_NUMBER { if ($1 > 65535) /* Unsigned */ 399 yyerror("invalid port number"); 400 else 401 $$ = $1; 402 } 403 | YY_STR { if (getport(NULL, $1, &($$)) == -1) 404 yyerror("invalid port number"); 405 $$ = ntohs($$); 406 } 407 ; 408 409 dport: | IPNY_PORT portspec { nat->in_pmin = htons($2); 410 nat->in_pmax = htons($2); } 411 | IPNY_PORT portspec '-' portspec { nat->in_pmin = htons($2); 412 nat->in_pmax = htons($4); } 413 | IPNY_PORT portspec ':' portspec { nat->in_pmin = htons($2); 414 nat->in_pmax = htons($4); } 415 ; 416 417 nport: IPNY_PORT portspec { nat->in_pnext = htons($2); } 418 | IPNY_PORT '=' portspec { nat->in_pnext = htons($3); 419 nat->in_flags |= IPN_FIXEDDPORT; 420 } 421 ; 422 423 ports: | IPNY_PORTS YY_NUMBER { nat->in_pmin = $2; } 424 | IPNY_PORTS IPNY_AUTO { nat->in_flags |= IPN_AUTOPORTMAP; } 425 ; 426 427 mapit: IPNY_MAP { nat->in_redir = NAT_MAP; } 428 | IPNY_BIMAP { nat->in_redir = NAT_BIMAP; } 429 ; 430 431 rdrit: IPNY_RDR { nat->in_redir = NAT_REDIRECT; } 432 ; 433 434 mapblockit: 435 IPNY_MAPBLOCK { nat->in_redir = NAT_MAPBLK; } 436 ; 437 438 mapfrom: 439 from sobject IPNY_TO dobject { if ($2 != 0 && $4 != 0 && $2 != $4) 440 yyerror("11.address family " 441 "mismatch"); 442 $$ = $2; 443 } 444 | from sobject '!' IPNY_TO dobject 445 { if ($2 != 0 && $5 != 0 && $2 != $5) 446 yyerror("12.address family " 447 "mismatch"); 448 nat->in_flags |= IPN_NOTDST; 449 $$ = $2; 450 } 451 ; 452 453 rdrfrom: 454 from sobject IPNY_TO dobject { if ($2 != 0 && $4 != 0 && $2 != $4) 455 yyerror("13.address family " 456 "mismatch"); 457 $$ = $2; 458 } 459 | '!' from sobject IPNY_TO dobject 460 { if ($3 != 0 && $5 != 0 && $3 != $5) 461 yyerror("14.address family " 462 "mismatch"); 463 nat->in_flags |= IPN_NOTSRC; 464 $$ = $3; 465 } 466 ; 467 468 from: IPNY_FROM { nat->in_flags |= IPN_FILTER; 469 yyexpectaddr = 1; } 470 ; 471 472 ifnames: 473 ifname { yyexpectaddr = 1; } 474 | ifname ',' otherifname { yyexpectaddr = 1; } 475 ; 476 477 ifname: YY_STR { strncpy(nat->in_ifnames[0], $1, 478 sizeof(nat->in_ifnames[0])); 479 nat->in_ifnames[0][LIFNAMSIZ - 1] = '\0'; 480 free($1); 481 } 482 ; 483 484 otherifname: 485 YY_STR { strncpy(nat->in_ifnames[1], $1, 486 sizeof(nat->in_ifnames[1])); 487 nat->in_ifnames[1][LIFNAMSIZ - 1] = '\0'; 488 free($1); 489 } 490 ; 491 492 mapport: 493 IPNY_PORTMAP tcpudp portspec ':' portspec 494 { nat->in_pmin = htons($3); 495 nat->in_pmax = htons($5); 496 } 497 | IPNY_PORTMAP tcpudp IPNY_AUTO 498 { nat->in_flags |= IPN_AUTOPORTMAP; 499 nat->in_pmin = htons(1024); 500 nat->in_pmax = htons(65535); 501 } 502 | IPNY_ICMPIDMAP YY_STR YY_NUMBER ':' YY_NUMBER 503 { if (strcmp($2, "icmp") != 0) { 504 yyerror("icmpidmap not followed by icmp"); 505 } 506 free($2); 507 if ($3 < 0 || $3 > 65535) 508 yyerror("invalid ICMP Id number"); 509 if ($5 < 0 || $5 > 65535) 510 yyerror("invalid ICMP Id number"); 511 nat->in_flags = IPN_ICMPQUERY; 512 nat->in_pmin = htons($3); 513 nat->in_pmax = htons($5); 514 } 515 ; 516 517 sobject: 518 saddr { $$ = $1; } 519 | saddr IPNY_PORT portstuff { nat->in_sport = $3.p1; 520 nat->in_stop = $3.p2; 521 nat->in_scmp = $3.pc; 522 $$ = $1; 523 } 524 ; 525 526 saddr: addr { if (nat->in_redir == NAT_REDIRECT) { 527 bcopy(&$1.a, &nat->in_src[0], 528 sizeof($1.a)); 529 bcopy(&$1.m, &nat->in_src[1], 530 sizeof($1.a)); 531 } else { 532 bcopy(&$1.a, &nat->in_in[0], 533 sizeof($1.a)); 534 bcopy(&$1.m, &nat->in_in[1], 535 sizeof($1.a)); 536 } 537 $$ = $1.v; 538 } 539 ; 540 541 dobject: 542 daddr { $$ = $1; } 543 | daddr IPNY_PORT portstuff { nat->in_dport = $3.p1; 544 nat->in_dtop = $3.p2; 545 nat->in_dcmp = $3.pc; 546 if (nat->in_redir == NAT_REDIRECT) 547 nat->in_pmin = htons($3.p1); 548 } 549 ; 550 551 daddr: addr { if (nat->in_redir == NAT_REDIRECT) { 552 bcopy(&$1.a, &nat->in_out[0], 553 sizeof($1.a)); 554 bcopy(&$1.m, &nat->in_out[1], 555 sizeof($1.a)); 556 } else { 557 bcopy(&$1.a, &nat->in_src[0], 558 sizeof($1.a)); 559 bcopy(&$1.m, &nat->in_src[1], 560 sizeof($1.a)); 561 } 562 $$ = $1.v; 563 } 564 ; 565 566 addr: IPNY_ANY { yyexpectaddr = 0; 567 bzero(&$$.a, sizeof($$.a)); 568 bzero(&$$.m, sizeof($$.a)); 569 $$.v = nat->in_v; 570 } 571 | hostname { $$.a = $1.a; 572 $$.v = $1.v; 573 if ($$.v == 4) { 574 $$.m.in4.s_addr = 0xffffffff; 575 } else { 576 $$.m.i6[0] = 0xffffffff; 577 $$.m.i6[1] = 0xffffffff; 578 $$.m.i6[2] = 0xffffffff; 579 $$.m.i6[3] = 0xffffffff; 580 } 581 yyexpectaddr = 0; 582 } 583 | hostname '/' YY_NUMBER { $$.a = $1.a; 584 if ($1.v == 0) { 585 if ($1.a.in4.s_addr != 0) 586 yyerror("invalid addr"); 587 if ($3 == 0 || $3 == 32) 588 $1.v = 4; 589 else if ($3 == 128) 590 $1.v = 6; 591 else 592 yyerror("invalid mask"); 593 nat->in_v = $1.v; 594 } 595 ntomask($1.v, $3, (u_32_t *)&$$.m); 596 $$.a.i6[0] &= $$.m.i6[0]; 597 $$.a.i6[1] &= $$.m.i6[1]; 598 $$.a.i6[2] &= $$.m.i6[2]; 599 $$.a.i6[3] &= $$.m.i6[3]; 600 $$.v = $1.v; 601 yyexpectaddr = 0; 602 } 603 | hostname '/' ipaddr { if ($1.v != $3.v) { 604 yyerror("1.address family " 605 "mismatch"); 606 } 607 $$.a = $1.a; 608 $$.m = $3.a; 609 $$.a.i6[0] &= $$.m.i6[0]; 610 $$.a.i6[1] &= $$.m.i6[1]; 611 $$.a.i6[2] &= $$.m.i6[2]; 612 $$.a.i6[3] &= $$.m.i6[3]; 613 $$.v = $1.v; 614 yyexpectaddr = 0; 615 } 616 | hostname '/' hexnumber { $$.a = $1.a; 617 $$.m.in4.s_addr = htonl($3); 618 $$.a.in4.s_addr &= $$.m.in4.s_addr; 619 $$.v = 4; 620 } 621 | hostname IPNY_MASK ipaddr { if ($1.v != $3.v) { 622 yyerror("2.address family " 623 "mismatch"); 624 } 625 $$.a = $1.a; 626 $$.m = $3.a; 627 $$.a.i6[0] &= $$.m.i6[0]; 628 $$.a.i6[1] &= $$.m.i6[1]; 629 $$.a.i6[2] &= $$.m.i6[2]; 630 $$.a.i6[3] &= $$.m.i6[3]; 631 $$.v = $1.v; 632 yyexpectaddr = 0; 633 } 634 | hostname IPNY_MASK hexnumber { $$.a = $1.a; 635 $$.m.in4.s_addr = htonl($3); 636 $$.a.in4.s_addr &= $$.m.in4.s_addr; 637 $$.v = 4; 638 } 639 ; 640 641 portstuff: 642 compare portspec { $$.pc = $1; $$.p1 = $2; } 643 | portspec range portspec { $$.pc = $2; $$.p1 = $1; $$.p2 = $3; } 644 ; 645 646 mapoptions: 647 rr frag age mssclamp nattag setproto 648 ; 649 650 rdroptions: 651 rr frag age sticky mssclamp rdrproxy nattag 652 ; 653 654 nattag: | IPNY_TAG YY_STR { strncpy(nat->in_tag.ipt_tag, $2, 655 sizeof(nat->in_tag.ipt_tag)); 656 } 657 rr: | IPNY_ROUNDROBIN { nat->in_flags |= IPN_ROUNDR; } 658 ; 659 660 frag: | IPNY_FRAG { nat->in_flags |= IPN_FRAG; } 661 ; 662 663 age: | IPNY_AGE YY_NUMBER { nat->in_age[0] = $2; 664 nat->in_age[1] = $2; } 665 | IPNY_AGE YY_NUMBER '/' YY_NUMBER { nat->in_age[0] = $2; 666 nat->in_age[1] = $4; } 667 ; 668 669 sticky: | IPNY_STICKY { if (!(nat->in_flags & IPN_ROUNDR) && 670 !(nat->in_flags & IPN_SPLIT)) { 671 fprintf(stderr, 672 "'sticky' for use with round-robin/IP splitting only\n"); 673 } else 674 nat->in_flags |= IPN_STICKY; 675 } 676 ; 677 678 mssclamp: 679 | IPNY_MSSCLAMP YY_NUMBER { nat->in_mssclamp = $2; } 680 ; 681 682 tcpudp: | IPNY_TCP { setnatproto(IPPROTO_TCP); } 683 | IPNY_UDP { setnatproto(IPPROTO_UDP); } 684 | IPNY_TCPUDP { nat->in_flags |= IPN_TCPUDP; 685 nat->in_p = 0; 686 } 687 | IPNY_TCP '/' IPNY_UDP { nat->in_flags |= IPN_TCPUDP; 688 nat->in_p = 0; 689 } 690 ; 691 692 rdrproxy: 693 IPNY_PROXY YY_STR 694 { strncpy(nat->in_plabel, $2, 695 sizeof(nat->in_plabel)); 696 nat->in_dport = nat->in_pnext; 697 nat->in_dport = htons(nat->in_dport); 698 free($2); 699 } 700 | proxy { if (nat->in_plabel[0] != '\0') { 701 nat->in_pmin = nat->in_dport; 702 nat->in_pmax = nat->in_pmin; 703 nat->in_pnext = nat->in_pmin; 704 } 705 } 706 ; 707 708 proto: YY_NUMBER { $$ = $1; } 709 | IPNY_TCP { $$ = IPPROTO_TCP; } 710 | IPNY_UDP { $$ = IPPROTO_UDP; } 711 | YY_STR { $$ = getproto($1); free($1); } 712 ; 713 714 hexnumber: 715 YY_HEX { $$ = $1; } 716 ; 717 718 hostname: 719 YY_STR { i6addr_t addr; 720 if (gethost($1, &addr, 0) == 0) { 721 $$.a = addr; 722 $$.v = 4; 723 } else 724 if (gethost($1, &addr, 1) == 0) { 725 $$.a = addr; 726 $$.v = 6; 727 } else { 728 yyerror("Unknown hostname"); 729 } 730 if ($$.v != 0) 731 nat->in_v = $$.v; 732 free($1); 733 } 734 | YY_NUMBER { bzero(&$$.a, sizeof($$.a)); 735 $$.a.in4.s_addr = htonl($1); 736 if ($$.a.in4.s_addr != 0) 737 $$.v = 4; 738 else 739 $$.v = nat->in_v; 740 if ($$.v != 0) 741 nat->in_v = $$.v; 742 } 743 | ipv4 { $$ = $1; 744 nat->in_v = 4; 745 } 746 | YY_IPV6 { $$.a = $1; 747 $$.v = 6; 748 nat->in_v = 6; 749 } 750 | YY_NUMBER YY_IPV6 { $$.a = $2; 751 $$.v = 6; 752 } 753 ; 754 755 compare: 756 '=' { $$ = FR_EQUAL; } 757 | YY_CMP_EQ { $$ = FR_EQUAL; } 758 | YY_CMP_NE { $$ = FR_NEQUAL; } 759 | YY_CMP_LT { $$ = FR_LESST; } 760 | YY_CMP_LE { $$ = FR_LESSTE; } 761 | YY_CMP_GT { $$ = FR_GREATERT; } 762 | YY_CMP_GE { $$ = FR_GREATERTE; } 763 764 range: 765 YY_RANGE_OUT { $$ = FR_OUTRANGE; } 766 | YY_RANGE_IN { $$ = FR_INRANGE; } 767 ; 768 769 ipaddr: ipv4 { $$ = $1; } 770 | YY_IPV6 { $$.a = $1; 771 $$.v = 6; 772 } 773 ; 774 775 ipv4: YY_NUMBER '.' YY_NUMBER '.' YY_NUMBER '.' YY_NUMBER 776 { if ($1 > 255 || $3 > 255 || $5 > 255 || $7 > 255) { 777 yyerror("Invalid octet string for IP address"); 778 return 0; 779 } 780 $$.a.in4.s_addr = ($1 << 24) | ($3 << 16) | ($5 << 8) | $7; 781 $$.a.in4.s_addr = htonl($$.a.in4.s_addr); 782 $$.v = 4; 783 } 784 ; 785 786 %% 787 788 789 static wordtab_t yywords[] = { 790 { "age", IPNY_AGE }, 791 { "any", IPNY_ANY }, 792 { "auto", IPNY_AUTO }, 793 { "bimap", IPNY_BIMAP }, 794 { "frag", IPNY_FRAG }, 795 { "from", IPNY_FROM }, 796 { "icmpidmap", IPNY_ICMPIDMAP }, 797 { "mask", IPNY_MASK }, 798 { "map", IPNY_MAP }, 799 { "map-block", IPNY_MAPBLOCK }, 800 { "mssclamp", IPNY_MSSCLAMP }, 801 { "netmask", IPNY_MASK }, 802 { "port", IPNY_PORT }, 803 { "portmap", IPNY_PORTMAP }, 804 { "ports", IPNY_PORTS }, 805 { "proxy", IPNY_PROXY }, 806 { "range", IPNY_RANGE }, 807 { "rdr", IPNY_RDR }, 808 { "round-robin",IPNY_ROUNDROBIN }, 809 { "sticky", IPNY_STICKY }, 810 { "tag", IPNY_TAG }, 811 { "tcp", IPNY_TCP }, 812 { "tcpudp", IPNY_TCPUDP }, 813 { "to", IPNY_TO }, 814 { "udp", IPNY_UDP }, 815 { "-", '-' }, 816 { "->", IPNY_TLATE }, 817 { "eq", YY_CMP_EQ }, 818 { "ne", YY_CMP_NE }, 819 { "lt", YY_CMP_LT }, 820 { "gt", YY_CMP_GT }, 821 { "le", YY_CMP_LE }, 822 { "ge", YY_CMP_GE }, 823 { NULL, 0 } 824 }; 825 826 827 int ipnat_parsefile(fd, addfunc, ioctlfunc, filename) 828 int fd; 829 addfunc_t addfunc; 830 ioctlfunc_t ioctlfunc; 831 char *filename; 832 { 833 FILE *fp = NULL; 834 char *s; 835 836 (void) yysettab(yywords); 837 838 s = getenv("YYDEBUG"); 839 if (s) 840 yydebug = atoi(s); 841 else 842 yydebug = 0; 843 844 if (strcmp(filename, "-")) { 845 fp = fopen(filename, "r"); 846 if (!fp) { 847 fprintf(stderr, "fopen(%s) failed: %s\n", filename, 848 STRERROR(errno)); 849 return -1; 850 } 851 } else 852 fp = stdin; 853 854 while (ipnat_parsesome(fd, addfunc, ioctlfunc, fp) == 1) 855 ; 856 if (fp != NULL) 857 fclose(fp); 858 return 0; 859 } 860 861 862 int ipnat_parsesome(fd, addfunc, ioctlfunc, fp) 863 int fd; 864 addfunc_t addfunc; 865 ioctlfunc_t ioctlfunc; 866 FILE *fp; 867 { 868 char *s; 869 int i; 870 871 yylineNum = 1; 872 873 natfd = fd; 874 nataddfunc = addfunc; 875 natioctlfunc = ioctlfunc; 876 877 if (feof(fp)) 878 return 0; 879 i = fgetc(fp); 880 if (i == EOF) 881 return 0; 882 if (ungetc(i, fp) == EOF) 883 return 0; 884 if (feof(fp)) 885 return 0; 886 s = getenv("YYDEBUG"); 887 if (s) 888 yydebug = atoi(s); 889 else 890 yydebug = 0; 891 892 yyin = fp; 893 yyparse(); 894 return 1; 895 } 896 897 898 static void newnatrule() 899 { 900 ipnat_t *n; 901 902 n = calloc(1, sizeof(*n)); 903 if (n == NULL) 904 return; 905 906 if (nat == NULL) 907 nattop = nat = n; 908 else { 909 nat->in_next = n; 910 nat = n; 911 } 912 } 913 914 915 static void setnatproto(p) 916 int p; 917 { 918 nat->in_p = p; 919 920 switch (p) 921 { 922 case IPPROTO_TCP : 923 nat->in_flags |= IPN_TCP; 924 nat->in_flags &= ~IPN_UDP; 925 break; 926 case IPPROTO_UDP : 927 nat->in_flags |= IPN_UDP; 928 nat->in_flags &= ~IPN_TCP; 929 break; 930 case IPPROTO_ICMP : 931 nat->in_flags &= ~IPN_TCPUDP; 932 if (!(nat->in_flags & IPN_ICMPQUERY)) { 933 nat->in_dcmp = 0; 934 nat->in_scmp = 0; 935 nat->in_pmin = 0; 936 nat->in_pmax = 0; 937 nat->in_pnext = 0; 938 } 939 break; 940 default : 941 if ((nat->in_redir & NAT_MAPBLK) == 0) { 942 /* Only reset dcmp/scmp in case dport/sport not set */ 943 if (0 == nat->in_tuc.ftu_dport) 944 nat->in_dcmp = 0; 945 if (0 == nat->in_tuc.ftu_sport) 946 nat->in_scmp = 0; 947 nat->in_pmin = 0; 948 nat->in_pmax = 0; 949 nat->in_pnext = 0; 950 nat->in_flags &= ~IPN_TCPUDP; 951 } 952 break; 953 } 954 955 if ((nat->in_flags & (IPN_TCPUDP|IPN_FIXEDDPORT)) == IPN_FIXEDDPORT) 956 nat->in_flags &= ~IPN_FIXEDDPORT; 957 } 958 959 960 void ipnat_addrule(fd, ioctlfunc, ptr) 961 int fd; 962 ioctlfunc_t ioctlfunc; 963 void *ptr; 964 { 965 ioctlcmd_t add, del; 966 ipfobj_t obj; 967 ipnat_t *ipn; 968 969 ipn = ptr; 970 bzero((char *)&obj, sizeof(obj)); 971 obj.ipfo_rev = IPFILTER_VERSION; 972 obj.ipfo_size = sizeof(ipnat_t); 973 obj.ipfo_type = IPFOBJ_IPNAT; 974 obj.ipfo_ptr = ptr; 975 add = 0; 976 del = 0; 977 978 if ((opts & OPT_DONOTHING) != 0) 979 fd = -1; 980 981 if (opts & OPT_ZERORULEST) { 982 add = SIOCZRLST; 983 } else if (opts & OPT_INACTIVE) { 984 add = SIOCADNAT; 985 del = SIOCRMNAT; 986 } else { 987 add = SIOCADNAT; 988 del = SIOCRMNAT; 989 } 990 991 if (ipn && (opts & OPT_VERBOSE)) 992 printnat(ipn, opts); 993 994 if (opts & OPT_DEBUG) 995 binprint(ipn, sizeof(*ipn)); 996 997 if ((opts & OPT_ZERORULEST) != 0) { 998 if ((*ioctlfunc)(fd, add, (void *)&obj) == -1) { 999 if ((opts & OPT_DONOTHING) == 0) { 1000 fprintf(stderr, "%d:", yylineNum); 1001 perror("ioctl(SIOCZRLST)"); 1002 } 1003 } else { 1004 #ifdef USE_QUAD_T 1005 /* 1006 printf("hits %qd bytes %qd ", 1007 (long long)fr->fr_hits, 1008 (long long)fr->fr_bytes); 1009 */ 1010 #else 1011 /* 1012 printf("hits %ld bytes %ld ", 1013 fr->fr_hits, fr->fr_bytes); 1014 */ 1015 #endif 1016 printnat(ipn, opts); 1017 } 1018 } else if ((opts & OPT_REMOVE) != 0) { 1019 if ((*ioctlfunc)(fd, del, (void *)&obj) == -1) { 1020 if ((opts & OPT_DONOTHING) == 0) { 1021 fprintf(stderr, "%d:", yylineNum); 1022 perror("ioctl(delete nat rule)"); 1023 } 1024 } 1025 } else { 1026 if ((*ioctlfunc)(fd, add, (void *)&obj) == -1) { 1027 if ((opts & OPT_DONOTHING) == 0) { 1028 fprintf(stderr, "%d:", yylineNum); 1029 perror("ioctl(add/insert nat rule)"); 1030 } 1031 } 1032 } 1033 } 1034 1035