ip_ioctl implements the ioctl system call on a ip device. 1) An ip channel for an
ip device is obtained by using the open system call eg Open("/dev/ip", whatever_mode_you_want).
The general description of what ioctl does depending on the request parameter are given in the ip.4 manual and therefore will not be covered here. The request parameter is the req parameter
in the ip_ioctl function. 2) ip_ioctl is also called by a lower level protocol interface
in order to configure the IP channel which the lower level protocol interface uses.

     1  /*
     2  ip_ioctl.c
       
     3  Copyright 1995 Philip Homburg
     4  */
       
     5  #include "inet.h"
     6  #include "buf.h"
     7  #include "event.h"
     8  #include "type.h"
       
     9  #include "arp.h"
    10  #include "assert.h"
    11  #include "clock.h"
    12  #include "icmp_lib.h"
    13  #include "ip.h"
    14  #include "ip_int.h"
    15  #include "ipr.h"
       
    16  THIS_FILE
       
    17  FORWARD int ip_checkopt ARGS(( ip_fd_t *ip_fd ));
    18  FORWARD void reply_thr_get ARGS(( ip_fd_t *ip_fd, size_t
    19          reply, int for_ioctl ));
       

ip_ioctl implements the IOCTL system call on a IP device.

    20  PUBLIC int ip_ioctl (fd, req)
    21  int fd;
    22  ioreq_t req;
    23  {
    24          ip_fd_t *ip_fd;
    25          ip_port_t *ip_port;
    26          nwio_ipopt_t *ipopt;
    27          nwio_ipopt_t oldopt, newopt;
    28          nwio_ipconf_t *ipconf;
    29          nwio_route_t *route_ent;
    30          acc_t *data;
    31          int result;
    32          unsigned int new_en_flags, new_di_flags,
    33                  old_en_flags, old_di_flags;
    34          unsigned long new_flags;
    35          int old_ip_flags;
    36          int ent_no;
       
    37          assert (fd>=0 && fd<=IP_FD_NR);

fd = index to ip channel in ip_fd_table. ip_fd = ip channel.

    38          ip_fd= &ip_fd_table[fd];
       
    39          assert (ip_fd->if_flags & IFF_INUSE);
       

If the IP device is called by a user process, ip_fd->if_get_userdata() gets the data passed to
the IP device by user. If ip_ioctl is called by a lower level protocol interface if_get_userdata()
gets the data which was coded for the lower level protocol interface to pass to the IP device.

As mentioned before, for an ioctl call, you can return an integer result to the user either by calling
sr_put_userdata(x, y, 0, 1) or sr_get_userdata(x, y, 0, 1) in sr.c. An integer result is returned to the user
by calling sr_get_userdata in sr.c by calling reply_thr_get. When data needs to return to the user
or lower level protocol interface a call to if_put_userdata precedes the call to return an integer result.
if_put_userdata copies the data from the inet process space to the user process space if a user process
made the ioctl system call.

    40          switch (req)
    41          {
    42          case NWIOSIPOPT:
    43                  data= (*ip_fd->if_get_userdata)(ip_fd->if_srfd, 0,
    44                          sizeof(nwio_ipopt_t), TRUE);
       
    45                  data= bf_packIffLess (data, sizeof(nwio_ipopt_t));
    46                  assert (data->acc_length == sizeof(nwio_ipopt_t));
       
    47                  ipopt= (nwio_ipopt_t *)ptr2acc_data(data);
    48                  oldopt= ip_fd->if_ipopt;
    49                  newopt= *ipopt;
       
    50                  old_en_flags= oldopt.nwio_flags & 0xffff;
    51                  old_di_flags= (oldopt.nwio_flags >> 16) & 0xffff;
    52                  new_en_flags= newopt.nwio_flags & 0xffff;
    53                  new_di_flags= (newopt.nwio_flags >> 16) & 0xffff;
    54                  if (new_en_flags & new_di_flags)
    55                  {
    56                          bf_afree(data);
    57                          reply_thr_get (ip_fd, EBADMODE, TRUE);
    58                          return NW_OK;
    59                  }
       
    60                  /* NWIO_ACC_MASK */
    61                  if (new_di_flags & NWIO_ACC_MASK)
    62                  {
    63                          bf_afree(data);
    64                          reply_thr_get (ip_fd, EBADMODE, TRUE);
    65                          return NW_OK;
    66                          /* access modes can't be disable */
    67                  }
       
    68                  if (!(new_en_flags & NWIO_ACC_MASK))
    69                          new_en_flags |= (old_en_flags & NWIO_ACC_MASK);
       
    70                  /* NWIO_LOC_MASK */
    71                  if (!((new_en_flags|new_di_flags) & NWIO_LOC_MASK))
    72                  {
    73                          new_en_flags |= (old_en_flags & NWIO_LOC_MASK);
    74                          new_di_flags |= (old_di_flags & NWIO_LOC_MASK);
    75                  }
       
    76                  /* NWIO_BROAD_MASK */
    77                  if (!((new_en_flags|new_di_flags) & NWIO_BROAD_MASK))
    78                  {
    79                          new_en_flags |= (old_en_flags & NWIO_BROAD_MASK);
    80                          new_di_flags |= (old_di_flags & NWIO_BROAD_MASK);
    81                  }
       
    82                  /* NWIO_REM_MASK */
    83                  if (!((new_en_flags|new_di_flags) & NWIO_REM_MASK))
    84                  {
    85                          new_en_flags |= (old_en_flags & NWIO_REM_MASK);
    86                          new_di_flags |= (old_di_flags & NWIO_REM_MASK);
    87                          newopt.nwio_rem= oldopt.nwio_rem;
    88                  }
       
    89                  /* NWIO_PROTO_MASK */
    90                  if (!((new_en_flags|new_di_flags) & NWIO_PROTO_MASK))
    91                  {
    92                          new_en_flags |= (old_en_flags & NWIO_PROTO_MASK);
    93                          new_di_flags |= (old_di_flags & NWIO_PROTO_MASK);
    94                          newopt.nwio_proto= oldopt.nwio_proto;
    95                  }
       
    96                  /* NWIO_HDR_O_MASK */
    97                  if (!((new_en_flags|new_di_flags) & NWIO_HDR_O_MASK))
    98                  {
    99                          new_en_flags |= (old_en_flags & NWIO_HDR_O_MASK);
   100                          new_di_flags |= (old_di_flags & NWIO_HDR_O_MASK);
   101                          newopt.nwio_tos= oldopt.nwio_tos;
   102                          newopt.nwio_ttl= oldopt.nwio_ttl;
   103                          newopt.nwio_df= oldopt.nwio_df;
   104                          newopt.nwio_hdropt= oldopt.nwio_hdropt;
   105                  }
       
   106                  /* NWIO_RW_MASK */
   107                  if (!((new_en_flags|new_di_flags) & NWIO_RW_MASK))
   108                  {
   109                          new_en_flags |= (old_en_flags & NWIO_RW_MASK);
   110                          new_di_flags |= (old_di_flags & NWIO_RW_MASK);
   111                  }
       
   112                  new_flags= ((unsigned long)new_di_flags << 16) | new_en_flags;
       
   113                  if ((new_flags & NWIO_RWDATONLY) && (new_flags &
   114                          (NWIO_REMANY|NWIO_PROTOANY|NWIO_HDR_O_ANY)))
   115                  {
   116                          bf_afree(data);
   117                          reply_thr_get(ip_fd, EBADMODE, TRUE);
   118                          return NW_OK;
   119                  }
       
   120                  if (ip_fd->if_flags & IFF_OPTSET)
   121                          ip_unhash_proto(ip_fd);
       
   122                  newopt.nwio_flags= new_flags;
   123                  ip_fd->if_ipopt= newopt;
       
   124                  result= ip_checkopt(ip_fd);
       
   125                  if (result<0)
   126                          ip_fd->if_ipopt= oldopt;
       
   127                  bf_afree(data);
   128                  reply_thr_get (ip_fd, result, TRUE);
   129                  return NW_OK;
       
   130          case NWIOGIPOPT:
   131                  data= bf_memreq(sizeof(nwio_ipopt_t));
       
   132                  ipopt= (nwio_ipopt_t *)ptr2acc_data(data);
       
   133                  *ipopt= ip_fd->if_ipopt;
       
   134                  result= (*ip_fd->if_put_userdata)(ip_fd->if_srfd, 0, data,
   135                                                                          TRUE);
   136                  return (*ip_fd->if_put_userdata)(ip_fd->if_srfd, result,
   137                                                          (acc_t *)0, TRUE);
       
   138          case NWIOSIPCONF:
   139                  ip_port= ip_fd->if_port;
       
   140                  data= (*ip_fd->if_get_userdata)(ip_fd->if_srfd, 0,
   141                                                  sizeof(nwio_ipconf_t), TRUE);
       
   142                  data= bf_packIffLess (data, sizeof(nwio_ipconf_t));
   143                  assert (data->acc_length == sizeof(nwio_ipconf_t));
       
   144                  old_ip_flags= ip_port->ip_flags;
       
   145                  ipconf= (nwio_ipconf_t *)ptr2acc_data(data);
       
   146                  if (ipconf->nwic_flags & ~NWIC_FLAGS)
   147                  {
   148                          bf_afree(data);
   149                          return (*ip_fd->if_put_userdata)(ip_fd-> if_srfd,
   150                                                  EBADMODE, (acc_t *)0, TRUE);
   151                  }
       
   152                  if (ipconf->nwic_flags & NWIC_IPADDR_SET)
   153                  {
   154                          ip_port->ip_ipaddr= ipconf->nwic_ipaddr;
   155                          ip_port->ip_flags |= IPF_IPADDRSET;
   156                          ip_port->ip_netmask=
   157                                  ip_netmask(ip_nettype(ipconf->nwic_ipaddr));
   158                          if (!(ip_port->ip_flags & IPF_NETMASKSET)) {
   159                                  ip_port->ip_subnetmask= ip_port->ip_netmask;
   160                          }
   161                          (*ip_port->ip_dev_set_ipaddr)(ip_port);
   162                  }
   163                  if (ipconf->nwic_flags & NWIC_NETMASK_SET)
   164                  {
   165                          ip_port->ip_subnetmask= ipconf->nwic_netmask;
   166                          ip_port->ip_flags |= IPF_NETMASKSET;
   167                  }
       
   168                  bf_afree(data);
   169                  return (*ip_fd->if_put_userdata)(ip_fd-> if_srfd, NW_OK,
   170                                                          (acc_t *)0, TRUE);
       
   171          case NWIOGIPCONF:
   172                  ip_port= ip_fd->if_port;
       
   173                  if (!(ip_port->ip_flags & IPF_IPADDRSET))
   174                  {
   175                          ip_fd->if_flags |= IFF_GIPCONF_IP;
   176                          return NW_SUSPEND;
   177                  }
   178                  ip_fd->if_flags &= ~IFF_GIPCONF_IP;
   179                  data= bf_memreq(sizeof(nwio_ipconf_t));
   180                  ipconf= (nwio_ipconf_t *)ptr2acc_data(data);
   181                  ipconf->nwic_flags= NWIC_IPADDR_SET;
   182                  ipconf->nwic_ipaddr= ip_port->ip_ipaddr;
   183                  ipconf->nwic_netmask= ip_port->ip_subnetmask;
   184                  if (ip_port->ip_flags & IPF_NETMASKSET)
   185                          ipconf->nwic_flags |= NWIC_NETMASK_SET;
       
   186                  result= (*ip_fd->if_put_userdata)(ip_fd->if_srfd, 0, data,
   187                                                                          TRUE);
   188                  return (*ip_fd->if_put_userdata)(ip_fd->if_srfd, result,
   189                                                          (acc_t *)0, TRUE);
   190         
   191          case NWIOGIPIROUTE:
   192                  data= (*ip_fd->if_get_userdata)(ip_fd->if_srfd,
   193                          0, sizeof(nwio_route_t), TRUE);
   194                  if (data == NULL)
   195                  {
   196                          return (*ip_fd->if_put_userdata)(ip_fd->if_srfd,
   197                                  EFAULT, NULL, TRUE);
   198                  }
       
   199                  data= bf_packIffLess (data, sizeof(nwio_route_t) );
   200                  route_ent= (nwio_route_t *)ptr2acc_data(data);
   201                  ent_no= route_ent->nwr_ent_no;
   202                  bf_afree(data);
       
   203                  data= bf_memreq(sizeof(nwio_route_t));
   204                  route_ent= (nwio_route_t *)ptr2acc_data(data);
   205                  result= ipr_get_iroute(ent_no, route_ent);
   206                  if (result < 0)
   207                          bf_afree(data);
   208                  else
   209                  {
   210                          assert(result == NW_OK);
   211                          result= (*ip_fd->if_put_userdata)(ip_fd->if_srfd, 0,
   212                                  data, TRUE);
   213                  }
   214                  return (*ip_fd->if_put_userdata)(ip_fd->if_srfd,
   215                          result, (acc_t *)0, TRUE);
       
   216          case NWIOSIPIROUTE:
   217                  data= (*ip_fd->if_get_userdata)(ip_fd->if_srfd,
   218                          0, sizeof(nwio_route_t), TRUE);
   219                  if (data == NULL)
   220                  {
   221                          return (*ip_fd->if_put_userdata)(ip_fd->if_srfd,
   222                                  EFAULT, NULL, TRUE);
   223                  }
       
   224                  data= bf_packIffLess (data, sizeof(nwio_route_t) );
   225                  route_ent= (nwio_route_t *)ptr2acc_data(data);
   226                  result= ipr_add_iroute(ip_fd->if_port-ip_port_table,
   227                          route_ent->nwr_dest, route_ent->nwr_netmask,
   228                          route_ent->nwr_gateway,
   229                          (route_ent->nwr_flags & NWRF_UNREACHABLE) ?
   230                                  IRTD_UNREACHABLE : route_ent->nwr_dist,
   231                          !!(route_ent->nwr_flags & NWRF_STATIC), NULL);
   232                  bf_afree(data);
       
   233                  return (*ip_fd->if_put_userdata)(ip_fd->if_srfd,
   234                          result, (acc_t *)0, TRUE);
       
   235          case NWIOGIPOROUTE:
   236                  data= (*ip_fd->if_get_userdata)(ip_fd->if_srfd,
   237                          0, sizeof(nwio_route_t), TRUE);
   238                  if (data == NULL)
   239                  {
   240                          return (*ip_fd->if_put_userdata)(ip_fd->if_srfd,
   241                                  EFAULT, NULL, TRUE);
   242                  }
       
   243                  data= bf_packIffLess (data, sizeof(nwio_route_t) );
   244                  route_ent= (nwio_route_t *)ptr2acc_data(data);
   245                  ent_no= route_ent->nwr_ent_no;
   246                  bf_afree(data);
       
   247                  data= bf_memreq(sizeof(nwio_route_t));
   248                  route_ent= (nwio_route_t *)ptr2acc_data(data);
   249                  result= ipr_get_oroute(ent_no, route_ent);
   250                  if (result < 0)
   251                          bf_afree(data);
   252                  else
   253                  {
   254                          assert(result == NW_OK);
   255                          result= (*ip_fd->if_put_userdata)(ip_fd->if_srfd, 0,
   256                                  data, TRUE);
   257                  }
   258                  return (*ip_fd->if_put_userdata)(ip_fd->if_srfd,
   259                          result, (acc_t *)0, TRUE);
       
   260          case NWIODIPIROUTE:
   261                  data= (*ip_fd->if_get_userdata)(ip_fd->if_srfd,
   262                          0, sizeof(nwio_route_t), TRUE);
   263                  if (data == NULL)
   264                  {
   265                          return (*ip_fd->if_put_userdata)(ip_fd->if_srfd,
   266                                  EFAULT, NULL, TRUE);
   267                  }
       
   268                  data= bf_packIffLess (data, sizeof(nwio_route_t) );
   269                  route_ent= (nwio_route_t *)ptr2acc_data(data);
   270                  result= ipr_del_iroute(ip_fd->if_port-ip_port_table,
   271                          route_ent->nwr_dest, route_ent->nwr_netmask,
   272                          route_ent->nwr_gateway,
   273                          (route_ent->nwr_flags & NWRF_UNREACHABLE) ?
   274                                  IRTD_UNREACHABLE : route_ent->nwr_dist,
   275                          !!(route_ent->nwr_flags & NWRF_STATIC));
   276                  bf_afree(data);
       
   277                  return (*ip_fd->if_put_userdata)(ip_fd->if_srfd,
   278                          result, (acc_t *)0, TRUE);
       
   279          case NWIOSIPOROUTE:
   280                  data= (*ip_fd->if_get_userdata)(ip_fd->if_srfd,
   281                          0, sizeof(nwio_route_t), TRUE);
   282                  if (data == NULL)
   283                  {
   284                          return (*ip_fd->if_put_userdata)(ip_fd->if_srfd,
   285                                  EFAULT, NULL, TRUE);
   286                  }
       
   287                  data= bf_packIffLess (data, sizeof(nwio_route_t) );
   288                  route_ent= (nwio_route_t *)ptr2acc_data(data);
   289                  result= ipr_add_oroute(ip_fd->if_port-ip_port_table,
   290                          route_ent->nwr_dest, route_ent->nwr_netmask,
   291                          route_ent->nwr_gateway, (time_t)0,
   292                          route_ent->nwr_dist,
   293                          !!(route_ent->nwr_flags & NWRF_STATIC),
   294                          route_ent->nwr_pref, NULL);
   295                  bf_afree(data);
       
   296                  return (*ip_fd->if_put_userdata)(ip_fd->if_srfd,
   297                          result, (acc_t *)0, TRUE);
       
   298          default:
   299                  break;
   300          }
   301          DBLOCK(1, printf("replying EBADIOCTL\n"));
   302          return (*ip_fd->if_put_userdata)(ip_fd-> if_srfd, EBADIOCTL,
   303                  (acc_t *)0, TRUE);
   304  }
       

ip_hash_proto() puts the ip_fd in the hash table ip_proto (if a protocol has been selected)
or in the linked list ip_proto_any (if a protocol has not been selected) of the port table
(if_port). ip_proto points to the hash table of ip channels which have selected a protocol. ip_proto_any
points to the head of the linked list which have not selected a protocol.

   305  PUBLIC void ip_hash_proto(ip_fd)
   306  ip_fd_t *ip_fd;
   307  {
   308          ip_port_t *ip_port;
   309          int hash;
       
   310          ip_port= ip_fd->if_port;
   311          if (ip_fd->if_ipopt.nwio_flags & NWIO_PROTOANY)
   312          {
   313                  ip_fd->if_proto_next= ip_port->ip_proto_any;
   314                  ip_port->ip_proto_any= ip_fd;
   315          }
   316          else
   317          {
   318                  hash= ip_fd->if_ipopt.nwio_proto & (IP_PROTO_HASH_NR-1);
   319                  ip_fd->if_proto_next= ip_port->ip_proto[hash];
   320                  ip_port->ip_proto[hash]= ip_fd;
   321          }
   322  }
       

ip_unhash_proto removes the ip_fd from the hash table ip_proto or linked list ip_proto_any of the port table (if_port).

   323  PUBLIC void ip_unhash_proto(ip_fd)
   324  ip_fd_t *ip_fd;
   325  {
   326          ip_port_t *ip_port;
   327          ip_fd_t *prev, *curr, **ip_fd_p;
   328          int hash;
       
   329          ip_port= ip_fd->if_port;
   330          if (ip_fd->if_ipopt.nwio_flags & NWIO_PROTOANY)
   331          {
   332                  ip_fd_p= &ip_port->ip_proto_any;
   333          }
   334          else
   335          {
   336                  hash= ip_fd->if_ipopt.nwio_proto & (IP_PROTO_HASH_NR-1);
   337                  ip_fd_p= &ip_port->ip_proto[hash];
   338          }
   339          for (prev= NULL, curr= *ip_fd_p; curr;
   340                  prev= curr, curr= curr->if_proto_next)
   341          {
   342                  if (curr == ip_fd)
   343                          break;
   344          }
   345          assert(curr);
   346          if (prev)
   347                  prev->if_proto_next= curr->if_proto_next;
   348          else
   349                  *ip_fd_p= curr->if_proto_next;
   350  }
       

ip_checkopt checks to make sure that the the ip options passed by the user are valid.
ip_checkopt is called by ip_ioctl on the NWIOSIPOPT request to configure the ip channel.

   351  PRIVATE int ip_checkopt (ip_fd)
   352  ip_fd_t *ip_fd;
   353  {
   354  /* bug: we don't check access modes yet */
       
   355          unsigned long flags;
   356          unsigned int en_di_flags;
   357          ip_port_t *port;
   358          acc_t *pack;
   359          int result;
       
   360          flags= ip_fd->if_ipopt.nwio_flags;
   361          en_di_flags= (flags >>16) | (flags & 0xffff);
       

NWIO_HDR_O_SPEC means all IP header options are specified in advance.

   362          if (flags & NWIO_HDR_O_SPEC)
   363          {
   364                  result= ip_chk_hdropt (ip_fd->if_ipopt.nwio_hdropt.iho_data,
   365                          ip_fd->if_ipopt.nwio_hdropt.iho_opt_siz);
   366                  if (result<0)
   367                          return result;
   368          }
   369          if ((en_di_flags & NWIO_ACC_MASK) &&
   370                  (en_di_flags & NWIO_LOC_MASK) &&
   371                  (en_di_flags & NWIO_BROAD_MASK) &&
   372                  (en_di_flags & NWIO_REM_MASK) &&
   373                  (en_di_flags & NWIO_PROTO_MASK) &&
   374                  (en_di_flags & NWIO_HDR_O_MASK) &&
   375                  (en_di_flags & NWIO_RW_MASK))
   376          {

ip_checkopt calls ip_hash_proto if the proposed configuration is valid.

   377                  ip_fd->if_flags |= IFF_OPTSET;
       
   378                  ip_hash_proto(ip_fd);
   379          }
       
   380          else
   381                  ip_fd->if_flags &= ~IFF_OPTSET;
       
   382          while (ip_fd->if_rdbuf_head)
   383          {
   384                  pack= ip_fd->if_rdbuf_head;
   385                  ip_fd->if_rdbuf_head= pack->acc_ext_link;
   386                  bf_afree(pack);
   387          }
   388          return NW_OK;
   389  }
       

reply_thr_get returns an integer result to the user by calling sr_get_userdata in sr.c.

   390  PRIVATE void reply_thr_get(ip_fd, reply, for_ioctl)
   391  ip_fd_t *ip_fd;
   392  size_t reply;
   393  int for_ioctl;
   394  {
   395          acc_t *result;
   396          result= (ip_fd->if_get_userdata)(ip_fd->if_srfd, reply,
   397                  (size_t)0, for_ioctl);
   398          assert (!result);
   399  }
       
   400  /*
   401   * $PchId: ip_ioctl.c,v 1.8 1996/12/17 07:56:18 philip Exp $
   402   */