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 IPNY_SEQUENTIAL 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 randport 494 { nat->in_pmin = htons($3); 495 nat->in_pmax = htons($5); 496 } 497 | IPNY_PORTMAP tcpudp IPNY_AUTO randport 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 randport: 518 | IPNY_SEQUENTIAL { nat->in_flags |= IPN_SEQUENTIAL; } 519 ; 520 521 sobject: 522 saddr { $$ = $1; } 523 | saddr IPNY_PORT portstuff { nat->in_sport = $3.p1; 524 nat->in_stop = $3.p2; 525 nat->in_scmp = $3.pc; 526 $$ = $1; 527 } 528 ; 529 530 saddr: addr { if (nat->in_redir == NAT_REDIRECT) { 531 bcopy(&$1.a, &nat->in_src[0], 532 sizeof($1.a)); 533 bcopy(&$1.m, &nat->in_src[1], 534 sizeof($1.a)); 535 } else { 536 bcopy(&$1.a, &nat->in_in[0], 537 sizeof($1.a)); 538 bcopy(&$1.m, &nat->in_in[1], 539 sizeof($1.a)); 540 } 541 $$ = $1.v; 542 } 543 ; 544 545 dobject: 546 daddr { $$ = $1; } 547 | daddr IPNY_PORT portstuff { nat->in_dport = $3.p1; 548 nat->in_dtop = $3.p2; 549 nat->in_dcmp = $3.pc; 550 if (nat->in_redir == NAT_REDIRECT) 551 nat->in_pmin = htons($3.p1); 552 } 553 ; 554 555 daddr: addr { if (nat->in_redir == NAT_REDIRECT) { 556 bcopy(&$1.a, &nat->in_out[0], 557 sizeof($1.a)); 558 bcopy(&$1.m, &nat->in_out[1], 559 sizeof($1.a)); 560 } else { 561 bcopy(&$1.a, &nat->in_src[0], 562 sizeof($1.a)); 563 bcopy(&$1.m, &nat->in_src[1], 564 sizeof($1.a)); 565 } 566 $$ = $1.v; 567 } 568 ; 569 570 addr: IPNY_ANY { yyexpectaddr = 0; 571 bzero(&$$.a, sizeof($$.a)); 572 bzero(&$$.m, sizeof($$.a)); 573 $$.v = nat->in_v; 574 } 575 | hostname { $$.a = $1.a; 576 $$.v = $1.v; 577 if ($$.v == 4) { 578 $$.m.in4.s_addr = 0xffffffff; 579 } else { 580 $$.m.i6[0] = 0xffffffff; 581 $$.m.i6[1] = 0xffffffff; 582 $$.m.i6[2] = 0xffffffff; 583 $$.m.i6[3] = 0xffffffff; 584 } 585 yyexpectaddr = 0; 586 } 587 | hostname '/' YY_NUMBER { $$.a = $1.a; 588 if ($1.v == 0) { 589 if ($1.a.in4.s_addr != 0) 590 yyerror("invalid addr"); 591 if ($3 == 0 || $3 == 32) 592 $1.v = 4; 593 else if ($3 == 128) 594 $1.v = 6; 595 else 596 yyerror("invalid mask"); 597 nat->in_v = $1.v; 598 } 599 ntomask($1.v, $3, (u_32_t *)&$$.m); 600 $$.a.i6[0] &= $$.m.i6[0]; 601 $$.a.i6[1] &= $$.m.i6[1]; 602 $$.a.i6[2] &= $$.m.i6[2]; 603 $$.a.i6[3] &= $$.m.i6[3]; 604 $$.v = $1.v; 605 yyexpectaddr = 0; 606 } 607 | hostname '/' ipaddr { if ($1.v != $3.v) { 608 yyerror("1.address family " 609 "mismatch"); 610 } 611 $$.a = $1.a; 612 $$.m = $3.a; 613 $$.a.i6[0] &= $$.m.i6[0]; 614 $$.a.i6[1] &= $$.m.i6[1]; 615 $$.a.i6[2] &= $$.m.i6[2]; 616 $$.a.i6[3] &= $$.m.i6[3]; 617 $$.v = $1.v; 618 yyexpectaddr = 0; 619 } 620 | hostname '/' hexnumber { $$.a = $1.a; 621 $$.m.in4.s_addr = htonl($3); 622 $$.a.in4.s_addr &= $$.m.in4.s_addr; 623 $$.v = 4; 624 } 625 | hostname IPNY_MASK ipaddr { if ($1.v != $3.v) { 626 yyerror("2.address family " 627 "mismatch"); 628 } 629 $$.a = $1.a; 630 $$.m = $3.a; 631 $$.a.i6[0] &= $$.m.i6[0]; 632 $$.a.i6[1] &= $$.m.i6[1]; 633 $$.a.i6[2] &= $$.m.i6[2]; 634 $$.a.i6[3] &= $$.m.i6[3]; 635 $$.v = $1.v; 636 yyexpectaddr = 0; 637 } 638 | hostname IPNY_MASK hexnumber { $$.a = $1.a; 639 $$.m.in4.s_addr = htonl($3); 640 $$.a.in4.s_addr &= $$.m.in4.s_addr; 641 $$.v = 4; 642 } 643 ; 644 645 portstuff: 646 compare portspec { $$.pc = $1; $$.p1 = $2; } 647 | portspec range portspec { $$.pc = $2; $$.p1 = $1; $$.p2 = $3; } 648 ; 649 650 mapoptions: 651 rr frag age mssclamp nattag setproto 652 ; 653 654 rdroptions: 655 rr frag age sticky mssclamp rdrproxy nattag 656 ; 657 658 nattag: | IPNY_TAG YY_STR { strncpy(nat->in_tag.ipt_tag, $2, 659 sizeof(nat->in_tag.ipt_tag)); 660 } 661 662 rr: | IPNY_ROUNDROBIN { nat->in_flags |= IPN_ROUNDR; } 663 ; 664 665 frag: | IPNY_FRAG { nat->in_flags |= IPN_FRAG; } 666 ; 667 668 age: | IPNY_AGE YY_NUMBER { nat->in_age[0] = $2; 669 nat->in_age[1] = $2; } 670 | IPNY_AGE YY_NUMBER '/' YY_NUMBER { nat->in_age[0] = $2; 671 nat->in_age[1] = $4; } 672 ; 673 674 sticky: | IPNY_STICKY { if (!(nat->in_flags & IPN_ROUNDR) && 675 !(nat->in_flags & IPN_SPLIT)) { 676 fprintf(stderr, 677 "'sticky' for use with round-robin/IP splitting only\n"); 678 } else 679 nat->in_flags |= IPN_STICKY; 680 } 681 ; 682 683 mssclamp: 684 | IPNY_MSSCLAMP YY_NUMBER { nat->in_mssclamp = $2; } 685 ; 686 687 tcpudp: | IPNY_TCP { setnatproto(IPPROTO_TCP); } 688 | IPNY_UDP { setnatproto(IPPROTO_UDP); } 689 | IPNY_TCPUDP { nat->in_flags |= IPN_TCPUDP; 690 nat->in_p = 0; 691 } 692 | IPNY_TCP '/' IPNY_UDP { nat->in_flags |= IPN_TCPUDP; 693 nat->in_p = 0; 694 } 695 ; 696 697 rdrproxy: 698 IPNY_PROXY YY_STR 699 { strncpy(nat->in_plabel, $2, 700 sizeof(nat->in_plabel)); 701 nat->in_dport = nat->in_pnext; 702 nat->in_dport = htons(nat->in_dport); 703 free($2); 704 } 705 | proxy { if (nat->in_plabel[0] != '\0') { 706 nat->in_pmin = nat->in_dport; 707 nat->in_pmax = nat->in_pmin; 708 nat->in_pnext = nat->in_pmin; 709 } 710 } 711 ; 712 713 proto: YY_NUMBER { $$ = $1; } 714 | IPNY_TCP { $$ = IPPROTO_TCP; } 715 | IPNY_UDP { $$ = IPPROTO_UDP; } 716 | YY_STR { $$ = getproto($1); free($1); } 717 ; 718 719 hexnumber: 720 YY_HEX { $$ = $1; } 721 ; 722 723 hostname: 724 YY_STR { i6addr_t addr; 725 if (gethost($1, &addr, 0) == 0) { 726 $$.a = addr; 727 $$.v = 4; 728 } else 729 if (gethost($1, &addr, 1) == 0) { 730 $$.a = addr; 731 $$.v = 6; 732 } else { 733 yyerror("Unknown hostname"); 734 } 735 if ($$.v != 0) 736 nat->in_v = $$.v; 737 free($1); 738 } 739 | YY_NUMBER { bzero(&$$.a, sizeof($$.a)); 740 $$.a.in4.s_addr = htonl($1); 741 if ($$.a.in4.s_addr != 0) 742 $$.v = 4; 743 else 744 $$.v = nat->in_v; 745 if ($$.v != 0) 746 nat->in_v = $$.v; 747 } 748 | ipv4 { $$ = $1; 749 nat->in_v = 4; 750 } 751 | YY_IPV6 { $$.a = $1; 752 $$.v = 6; 753 nat->in_v = 6; 754 } 755 | YY_NUMBER YY_IPV6 { $$.a = $2; 756 $$.v = 6; 757 } 758 ; 759 760 compare: 761 '=' { $$ = FR_EQUAL; } 762 | YY_CMP_EQ { $$ = FR_EQUAL; } 763 | YY_CMP_NE { $$ = FR_NEQUAL; } 764 | YY_CMP_LT { $$ = FR_LESST; } 765 | YY_CMP_LE { $$ = FR_LESSTE; } 766 | YY_CMP_GT { $$ = FR_GREATERT; } 767 | YY_CMP_GE { $$ = FR_GREATERTE; } 768 769 range: 770 YY_RANGE_OUT { $$ = FR_OUTRANGE; } 771 | YY_RANGE_IN { $$ = FR_INRANGE; } 772 ; 773 774 ipaddr: ipv4 { $$ = $1; } 775 | YY_IPV6 { $$.a = $1; 776 $$.v = 6; 777 } 778 ; 779 780 ipv4: YY_NUMBER '.' YY_NUMBER '.' YY_NUMBER '.' YY_NUMBER 781 { if ($1 > 255 || $3 > 255 || $5 > 255 || $7 > 255) { 782 yyerror("Invalid octet string for IP address"); 783 return 0; 784 } 785 $$.a.in4.s_addr = ($1 << 24) | ($3 << 16) | ($5 << 8) | $7; 786 $$.a.in4.s_addr = htonl($$.a.in4.s_addr); 787 $$.v = 4; 788 } 789 ; 790 791 %% 792 793 794 static wordtab_t yywords[] = { 795 { "age", IPNY_AGE }, 796 { "any", IPNY_ANY }, 797 { "auto", IPNY_AUTO }, 798 { "bimap", IPNY_BIMAP }, 799 { "frag", IPNY_FRAG }, 800 { "from", IPNY_FROM }, 801 { "icmpidmap", IPNY_ICMPIDMAP }, 802 { "mask", IPNY_MASK }, 803 { "map", IPNY_MAP }, 804 { "map-block", IPNY_MAPBLOCK }, 805 { "mssclamp", IPNY_MSSCLAMP }, 806 { "netmask", IPNY_MASK }, 807 { "port", IPNY_PORT }, 808 { "portmap", IPNY_PORTMAP }, 809 { "ports", IPNY_PORTS }, 810 { "proxy", IPNY_PROXY }, 811 { "range", IPNY_RANGE }, 812 { "rdr", IPNY_RDR }, 813 { "round-robin",IPNY_ROUNDROBIN }, 814 { "sequential", IPNY_SEQUENTIAL }, 815 { "sticky", IPNY_STICKY }, 816 { "tag", IPNY_TAG }, 817 { "tcp", IPNY_TCP }, 818 { "tcpudp", IPNY_TCPUDP }, 819 { "to", IPNY_TO }, 820 { "udp", IPNY_UDP }, 821 { "-", '-' }, 822 { "->", IPNY_TLATE }, 823 { "eq", YY_CMP_EQ }, 824 { "ne", YY_CMP_NE }, 825 { "lt", YY_CMP_LT }, 826 { "gt", YY_CMP_GT }, 827 { "le", YY_CMP_LE }, 828 { "ge", YY_CMP_GE }, 829 { NULL, 0 } 830 }; 831 832 833 int ipnat_parsefile(fd, addfunc, ioctlfunc, filename) 834 int fd; 835 addfunc_t addfunc; 836 ioctlfunc_t ioctlfunc; 837 char *filename; 838 { 839 FILE *fp = NULL; 840 char *s; 841 842 (void) yysettab(yywords); 843 844 s = getenv("YYDEBUG"); 845 if (s) 846 yydebug = atoi(s); 847 else 848 yydebug = 0; 849 850 if (strcmp(filename, "-")) { 851 fp = fopen(filename, "r"); 852 if (!fp) { 853 fprintf(stderr, "fopen(%s) failed: %s\n", filename, 854 STRERROR(errno)); 855 return -1; 856 } 857 } else 858 fp = stdin; 859 860 while (ipnat_parsesome(fd, addfunc, ioctlfunc, fp) == 1) 861 ; 862 if (fp != NULL) 863 fclose(fp); 864 return 0; 865 } 866 867 868 int ipnat_parsesome(fd, addfunc, ioctlfunc, fp) 869 int fd; 870 addfunc_t addfunc; 871 ioctlfunc_t ioctlfunc; 872 FILE *fp; 873 { 874 char *s; 875 int i; 876 877 yylineNum = 1; 878 879 natfd = fd; 880 nataddfunc = addfunc; 881 natioctlfunc = ioctlfunc; 882 883 if (feof(fp)) 884 return 0; 885 i = fgetc(fp); 886 if (i == EOF) 887 return 0; 888 if (ungetc(i, fp) == EOF) 889 return 0; 890 if (feof(fp)) 891 return 0; 892 s = getenv("YYDEBUG"); 893 if (s) 894 yydebug = atoi(s); 895 else 896 yydebug = 0; 897 898 yyin = fp; 899 yyparse(); 900 return 1; 901 } 902 903 904 static void newnatrule() 905 { 906 ipnat_t *n; 907 908 n = calloc(1, sizeof(*n)); 909 if (n == NULL) 910 return; 911 912 if (nat == NULL) 913 nattop = nat = n; 914 else { 915 nat->in_next = n; 916 nat = n; 917 } 918 } 919 920 921 static void setnatproto(p) 922 int p; 923 { 924 nat->in_p = p; 925 926 switch (p) 927 { 928 case IPPROTO_TCP : 929 nat->in_flags |= IPN_TCP; 930 nat->in_flags &= ~IPN_UDP; 931 break; 932 case IPPROTO_UDP : 933 nat->in_flags |= IPN_UDP; 934 nat->in_flags &= ~IPN_TCP; 935 break; 936 case IPPROTO_ICMP : 937 nat->in_flags &= ~IPN_TCPUDP; 938 if (!(nat->in_flags & IPN_ICMPQUERY)) { 939 nat->in_dcmp = 0; 940 nat->in_scmp = 0; 941 nat->in_pmin = 0; 942 nat->in_pmax = 0; 943 nat->in_pnext = 0; 944 } 945 break; 946 default : 947 if ((nat->in_redir & NAT_MAPBLK) == 0) { 948 /* Only reset dcmp/scmp in case dport/sport not set */ 949 if (0 == nat->in_tuc.ftu_dport) 950 nat->in_dcmp = 0; 951 if (0 == nat->in_tuc.ftu_sport) 952 nat->in_scmp = 0; 953 nat->in_pmin = 0; 954 nat->in_pmax = 0; 955 nat->in_pnext = 0; 956 nat->in_flags &= ~IPN_TCPUDP; 957 } 958 break; 959 } 960 961 if ((nat->in_flags & (IPN_TCPUDP|IPN_FIXEDDPORT)) == IPN_FIXEDDPORT) 962 nat->in_flags &= ~IPN_FIXEDDPORT; 963 } 964 965 966 void ipnat_addrule(fd, ioctlfunc, ptr) 967 int fd; 968 ioctlfunc_t ioctlfunc; 969 void *ptr; 970 { 971 ioctlcmd_t add, del; 972 ipfobj_t obj; 973 ipnat_t *ipn; 974 975 ipn = ptr; 976 bzero((char *)&obj, sizeof(obj)); 977 obj.ipfo_rev = IPFILTER_VERSION; 978 obj.ipfo_size = sizeof(ipnat_t); 979 obj.ipfo_type = IPFOBJ_IPNAT; 980 obj.ipfo_ptr = ptr; 981 add = 0; 982 del = 0; 983 984 if ((opts & OPT_DONOTHING) != 0) 985 fd = -1; 986 987 if (opts & OPT_ZERORULEST) { 988 add = SIOCZRLST; 989 } else if (opts & OPT_INACTIVE) { 990 add = SIOCADNAT; 991 del = SIOCRMNAT; 992 } else { 993 add = SIOCADNAT; 994 del = SIOCRMNAT; 995 } 996 997 if (ipn && (opts & OPT_VERBOSE)) 998 printnat(ipn, opts); 999 1000 if (opts & OPT_DEBUG) 1001 binprint(ipn, sizeof(*ipn)); 1002 1003 if ((opts & OPT_ZERORULEST) != 0) { 1004 if ((*ioctlfunc)(fd, add, (void *)&obj) == -1) { 1005 if ((opts & OPT_DONOTHING) == 0) { 1006 fprintf(stderr, "%d:", yylineNum); 1007 perror("ioctl(SIOCZRLST)"); 1008 } 1009 } else { 1010 #ifdef USE_QUAD_T 1011 /* 1012 printf("hits %qd bytes %qd ", 1013 (long long)fr->fr_hits, 1014 (long long)fr->fr_bytes); 1015 */ 1016 #else 1017 /* 1018 printf("hits %ld bytes %ld ", 1019 fr->fr_hits, fr->fr_bytes); 1020 */ 1021 #endif 1022 printnat(ipn, opts); 1023 } 1024 } else if ((opts & OPT_REMOVE) != 0) { 1025 if ((*ioctlfunc)(fd, del, (void *)&obj) == -1) { 1026 if ((opts & OPT_DONOTHING) == 0) { 1027 fprintf(stderr, "%d:", yylineNum); 1028 perror("ioctl(delete nat rule)"); 1029 } 1030 } 1031 } else { 1032 if ((*ioctlfunc)(fd, add, (void *)&obj) == -1) { 1033 if ((opts & OPT_DONOTHING) == 0) { 1034 fprintf(stderr, "%d:", yylineNum); 1035 perror("ioctl(add/insert nat rule)"); 1036 } 1037 } 1038 } 1039 } 1040 1041