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