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