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