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 */
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.