Add the DragonFly cvs id and perform general cleanups on cvs/rcs/sccs ids. Most
[dragonfly.git] / usr.sbin / atm / atmarpd / atmarp_scsp.c
1 /*
2  *
3  * ===================================
4  * HARP  |  Host ATM Research Platform
5  * ===================================
6  *
7  *
8  * This Host ATM Research Platform ("HARP") file (the "Software") is
9  * made available by Network Computing Services, Inc. ("NetworkCS")
10  * "AS IS".  NetworkCS does not provide maintenance, improvements or
11  * support of any kind.
12  *
13  * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
14  * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
15  * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
16  * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
17  * In no event shall NetworkCS be responsible for any damages, including
18  * but not limited to consequential damages, arising from or relating to
19  * any use of the Software or related support.
20  *
21  * Copyright 1994-1998 Network Computing Services, Inc.
22  *
23  * Copies of this Software may be made, however, the above copyright
24  * notice must be reproduced on all copies.
25  *
26  *      @(#) $FreeBSD: src/usr.sbin/atm/atmarpd/atmarp_scsp.c,v 1.3 1999/08/28 01:15:30 peter Exp $
27  *      @(#) $DragonFly: src/usr.sbin/atm/atmarpd/atmarp_scsp.c,v 1.2 2003/06/17 04:29:52 dillon Exp $
28  */
29
30 /*
31  * Server Cache Synchronization Protocol (SCSP) Support
32  * ----------------------------------------------------
33  *
34  * SCSP-ATMARP server interface: SCSP/ATMARP interface code
35  *
36  */
37
38 #include <sys/types.h>
39 #include <sys/param.h>
40 #include <sys/socket.h>
41 #include <net/if.h>
42 #include <netinet/in.h>
43 #include <netatm/port.h>
44 #include <netatm/queue.h>
45 #include <netatm/atm.h>
46 #include <netatm/atm_if.h>
47 #include <netatm/atm_sap.h>
48 #include <netatm/atm_sys.h>
49 #include <netatm/atm_ioctl.h>
50 #include <netatm/uni/uniip_var.h>
51  
52 #include <errno.h>
53 #include <fcntl.h>
54 #include <libatm.h>
55 #include <stdio.h>
56 #include <stdlib.h>
57 #include <string.h>
58 #include <syslog.h>
59 #include <unistd.h>
60
61 #include "../scspd/scsp_msg.h"
62 #include "../scspd/scsp_if.h"
63 #include "../scspd/scsp_var.h"
64 #include "atmarp_var.h"
65
66 /*
67  * Send the cache for a LIS to SCSP
68  *
69  *
70  * Arguments:
71  *      aip     pointer to interface block
72  *
73  * Returns:
74  *      0       cache sent to SCSP OK
75  *      errno   reason for failure
76  *
77  */
78 int
79 atmarp_scsp_cache(aip, msg)
80         Atmarp_intf     *aip;
81         Scsp_if_msg     *msg;
82 {
83         int             i, len, rc = 0;
84         Atmarp          *aap;
85         Scsp_if_msg     *smp = (Scsp_if_msg *)0;
86         Scsp_atmarp_msg *sap;
87
88         /*
89          * Figure out how big the message needs to be
90          */
91         len = sizeof(Scsp_if_msg_hdr);
92         for (i = 0; i < ATMARP_HASHSIZ; i++) {
93                 for (aap = aip->ai_arptbl[i]; aap; aap = aap->aa_next) {
94                         len += sizeof(Scsp_atmarp_msg);
95                 }
96         }
97
98         /*
99          * Get memory for the cache message
100          */
101         smp = (Scsp_if_msg *)UM_ALLOC(len);
102         if (!smp) {
103                 atmarp_mem_err("atmarp_scsp_cache: len");
104         }
105         UM_ZERO(smp, len);
106
107         /*
108          * Set header fields in SCSP message
109          */
110         smp->si_type = SCSP_CACHE_RSP;
111         smp->si_proto = SCSP_PROTO_ATMARP;
112         smp->si_len = len;
113         smp->si_tok = msg->si_tok;
114
115         /*
116          * Loop through the cache, adding each entry to the SCSP
117          * Cache Response message
118          */
119         sap = &smp->si_atmarp;
120         for (i = 0; i < ATMARP_HASHSIZ; i++) {
121                 for (aap = aip->ai_arptbl[i]; aap; aap = aap->aa_next) {
122                         sap->sa_state = SCSP_ASTATE_NEW;
123                         sap->sa_cpa = aap->aa_dstip;
124                         ATM_ADDR_COPY(&aap->aa_dstatm, &sap->sa_cha);
125                         ATM_ADDR_COPY(&aap->aa_dstatmsub, &sap->sa_csa);
126                         sap->sa_key = aap->aa_key;
127                         sap->sa_oid = aap->aa_oid;
128                         sap->sa_seq = aap->aa_seq;
129                         sap++;
130                 }
131         }
132
133         /*
134          * Send the message to SCSP
135          */
136         rc = atmarp_scsp_out(aip, (char *)smp, len);
137
138         /*
139          * Free the message
140          */
141         if (smp)
142                 UM_FREE(smp);
143
144         return(rc);
145 }
146
147
148 /*
149  * Answer a reqeust for information about a cache entry
150  *
151  * Arguments:
152  *      aap     pointer to entry
153  *      state   entry's new state
154  *
155  * Returns:
156  *      0       success
157  *      errno   reason for failure
158  *
159  */
160 int
161 atmarp_scsp_solicit(aip, smp)
162         Atmarp_intf     *aip;
163         Scsp_if_msg     *smp;
164 {
165         int             i, rc = 0;
166         Atmarp          *aap;
167         Scsp_if_msg     *rsp = (Scsp_if_msg *)0;
168
169         /*
170          * Search the interface's ATMARP cache for an entry with
171          * the specified cache key and origin ID
172          */
173         for (i = 0; i < ATMARP_HASHSIZ; i++) {
174                 for (aap = aip->ai_arptbl[i]; aap; aap = aap->aa_next) {
175                         if (KEY_EQUAL(&aap->aa_key,
176                                         &smp->si_sum.ss_key) &&
177                                         OID_EQUAL(&aap->aa_oid,
178                                         &smp->si_sum.ss_oid))
179                                 break;
180                 }
181                 if (aap)
182                         break;
183         }
184
185         /*
186          * Get storage for a Solicit Response
187          */
188         rsp = (Scsp_if_msg *)UM_ALLOC(sizeof(Scsp_if_msg));
189         if (!rsp) {
190                 atmarp_mem_err("atmarp_scsp_solicit: sizeof(Scsp_if_msg)");
191         }
192         UM_ZERO(rsp, sizeof(Scsp_if_msg));
193
194         /*
195          * Fill out the Solicit Rsp
196          */
197         rsp->si_type = SCSP_SOLICIT_RSP;
198         rsp->si_proto = smp->si_proto;
199         rsp->si_tok = smp->si_tok;
200
201         if (aap) {
202                 /*
203                  * Copy fields from the ATMARP entry to the SCSP
204                  * Update Request message
205                  */
206                 rsp->si_rc = SCSP_RSP_OK;
207                 rsp->si_len = sizeof(Scsp_if_msg_hdr) +
208                                 sizeof(Scsp_atmarp_msg);
209                 rsp->si_atmarp.sa_state = SCSP_ASTATE_UPD;
210                 rsp->si_atmarp.sa_cpa = aap->aa_dstip;
211                 ATM_ADDR_COPY(&aap->aa_dstatm, &rsp->si_atmarp.sa_cha);
212                 ATM_ADDR_COPY(&aap->aa_dstatmsub, &rsp->si_atmarp.sa_csa);
213                 rsp->si_atmarp.sa_key = aap->aa_key;
214                 rsp->si_atmarp.sa_oid = aap->aa_oid;
215                 rsp->si_atmarp.sa_seq = aap->aa_seq;
216         } else {
217                 /*
218                  * Entry not found--set return code
219                  */
220                 rsp->si_rc = SCSP_RSP_NOT_FOUND;
221                 rsp->si_len = smp->si_len;
222                 rsp->si_sum = smp->si_sum;
223         }
224
225         /*
226          * Send the message to SCSP
227          */
228         rc = atmarp_scsp_out(aip, (char *)rsp, rsp->si_len);
229         UM_FREE(rsp);
230
231         return(rc);
232 }
233
234
235 /*
236  * Send a cache update to SCSP
237  *
238  * Arguments:
239  *      aap     pointer to entry
240  *      state   entry's new state
241  *
242  * Returns:
243  *      0       success
244  *      errno   reason for failure
245  *
246  */
247 int
248 atmarp_scsp_update(aap, state)
249         Atmarp          *aap;
250         int             state;
251 {
252         int             rc = 0;
253         Atmarp_intf     *aip = aap->aa_intf;
254         Scsp_if_msg     *smp = (Scsp_if_msg *)0;
255
256         /*
257          * Make sure the connection to SCSP is active
258          */
259         if (aip->ai_state == AI_STATE_NULL) {
260                 return(0);
261         }
262
263         /*
264          * Get memory for the cache message
265          */
266         smp = (Scsp_if_msg *)UM_ALLOC(sizeof(Scsp_if_msg));
267         if (!smp) {
268                 atmarp_mem_err("atmarp_scsp_update: sizeof(Scsp_if_msg)");
269         }
270         UM_ZERO(smp, sizeof(Scsp_if_msg));
271
272         /*
273          * Set header fields in SCSP message
274          */
275         smp->si_type = SCSP_UPDATE_REQ;
276         smp->si_proto = SCSP_PROTO_ATMARP;
277         smp->si_len = sizeof(Scsp_if_msg_hdr) + sizeof(Scsp_atmarp_msg);
278
279         /*
280          * Copy fields from the ATMARP entry to the SCSP
281          * Update Request message
282          */
283         smp->si_atmarp.sa_state = state;
284         smp->si_atmarp.sa_cpa = aap->aa_dstip;
285         ATM_ADDR_COPY(&aap->aa_dstatm, &smp->si_atmarp.sa_cha);
286         ATM_ADDR_COPY(&aap->aa_dstatmsub, &smp->si_atmarp.sa_csa);
287         smp->si_atmarp.sa_key = aap->aa_key;
288         smp->si_atmarp.sa_oid = aap->aa_oid;
289         smp->si_atmarp.sa_seq = aap->aa_seq;
290
291         /*
292          * Send the message to SCSP
293          */
294         rc = atmarp_scsp_out(aap->aa_intf, (char *)smp, smp->si_len);
295
296         UM_FREE(smp);
297         return(rc);
298 }
299
300
301 /*
302  * Respond to a Cache Update Indication from SCSP
303  *
304  *
305  * Arguments:
306  *      aip     pointer to interface control block
307  *      smp     pointer to message from SCSP
308  *
309  * Returns:
310  *      0       Message processed OK
311  *      errno   Reason for failure
312  *
313  */
314 int
315 atmarp_scsp_update_in(aip, smp)
316         Atmarp_intf     *aip;
317         Scsp_if_msg     *smp;
318 {
319         int     accept, rc;
320         Atmarp  *aap;
321
322         /*
323          * Look up the entry
324          */
325         ATMARP_LOOKUP(aip, smp->si_atmarp.sa_cpa.s_addr, aap);
326
327         /*
328          * Whether we accept the request depends on whether we
329          * already have an entry for it
330          */
331          if (!aap) {
332                 /*
333                  * We don't have this entry--accept it
334                  */
335                 accept = 1;
336         } else {
337                 /*
338                  * We do have an entry for this host--check the
339                  * origin ID
340                  */
341                 if (bcmp(&aip->ai_ip_addr.s_addr,
342                                 smp->si_atmarp.sa_oid.id,
343                                 SCSP_ATMARP_ID_LEN) == 0) {
344                         /*
345                          * The received entry originated with us--
346                          * reject it
347                          */
348                         accept = 0;
349                 } else if (bcmp(&aip->ai_ip_addr.s_addr,
350                                 aap->aa_oid.id,
351                                 SCSP_ATMARP_ID_LEN) == 0) {
352                         /*
353                          * We originated the entry we currently have--
354                          * only accept the new one if SCSP has higher
355                          * priority than the existing entry
356                          */
357                         accept = aap->aa_origin < UAO_SCSP;
358                 } else {
359                         /*
360                          * Accept the entry if it is more up-to-date
361                          * than the existing entry
362                          */
363                         accept = KEY_EQUAL(&aap->aa_key,
364                                         &smp->si_atmarp.sa_key) &&
365                                 OID_EQUAL(&aap->aa_oid,
366                                         &smp->si_atmarp.sa_oid) &&
367                                 (aap->aa_seq < smp->si_atmarp.sa_seq);
368                 }
369         }
370
371         /*
372          * Add the entry to the cache, if appropriate
373          */
374         if (accept) {
375                 if (!aap) {
376                         /*
377                          * Copy info from SCSP to a new cache entry
378                          */
379                         aap = (Atmarp *)UM_ALLOC(sizeof(Atmarp));
380                         if (!aap)
381                                 atmarp_mem_err("atmarp_scsp_update_in: sizeof(Atmarp)");
382                         UM_ZERO(aap, sizeof(Atmarp));
383
384                         aap->aa_dstip = smp->si_atmarp.sa_cpa;
385                         aap->aa_dstatm = smp->si_atmarp.sa_cha;
386                         aap->aa_dstatmsub = smp->si_atmarp.sa_csa;
387                         aap->aa_key = smp->si_atmarp.sa_key;
388                         aap->aa_oid = smp->si_atmarp.sa_oid;
389                         aap->aa_seq = smp->si_atmarp.sa_seq;
390                         aap->aa_intf = aip;
391                         aap->aa_origin = UAO_SCSP;
392
393                         /*
394                          * Add the new entry to our cache
395                          */
396                         ATMARP_ADD(aip, aap);
397                 } else {
398                         /*
399                          * Update the existing entry
400                          */
401                         aap->aa_dstip = smp->si_atmarp.sa_cpa;
402                         aap->aa_dstatm = smp->si_atmarp.sa_cha;
403                         aap->aa_dstatmsub = smp->si_atmarp.sa_csa;
404                         aap->aa_key = smp->si_atmarp.sa_key;
405                         aap->aa_oid = smp->si_atmarp.sa_oid;
406                         aap->aa_seq = smp->si_atmarp.sa_seq;
407                         aap->aa_origin = UAO_SCSP;
408                 }
409
410                 /*
411                  * Send the updated entry to the kernel
412                  */
413                 if (atmarp_update_kernel(aap) == 0)
414                         rc = SCSP_RSP_OK;
415                 else
416                         rc = SCSP_RSP_REJ;
417         } else {
418                 rc = SCSP_RSP_REJ;
419         }
420
421         /*
422          * Turn the received message into a response
423          */
424         smp->si_type = SCSP_UPDATE_RSP;
425         smp->si_rc = rc;
426
427         /*
428          * Send the message to SCSP
429          */
430         rc = atmarp_scsp_out(aip, (char *)smp, smp->si_len);
431
432         return(rc);
433 }
434
435
436 /*
437  * Read and process a message from SCSP
438  *
439  *
440  * Arguments:
441  *      aip     interface for read
442  *
443  * Returns:
444  *      0       success
445  *      errno   reason for failure
446  *
447  */
448 int
449 atmarp_scsp_read(aip)
450         Atmarp_intf     *aip;
451 {
452         int             len, rc = 0;
453         char            *buff = (char *)0;
454         Scsp_if_msg     *smp;
455         Scsp_if_msg_hdr msg_hdr;
456
457         /*
458          * Read the header of the message from SCSP
459          */
460         len = read(aip->ai_scsp_sock, (char *)&msg_hdr,
461                         sizeof(msg_hdr));
462         if (len == -1) {
463                 rc = errno;
464                 goto read_fail;
465         } else if (len != sizeof(msg_hdr)) {
466                 rc = EMSGSIZE;
467                 goto read_fail;
468         }
469
470         /*
471          * Get a buffer that will hold the message
472          */
473         buff = UM_ALLOC(msg_hdr.sh_len);
474         if (!buff)
475                 atmarp_mem_err("atmarp_scsp_read: msg_hdr.sh_len");
476         UM_COPY(&msg_hdr, buff, sizeof(msg_hdr));
477
478         /*
479          * Read the rest of the message, if there is more than
480          * just a header
481          */
482         len = msg_hdr.sh_len - sizeof(msg_hdr);
483         if (len > 0) {
484                 len = read(aip->ai_scsp_sock, buff + sizeof(msg_hdr),
485                                 len);
486                 if (len == -1) {
487                         rc = errno;
488                         goto read_fail;
489                 } else if (len != msg_hdr.sh_len - sizeof(msg_hdr)) {
490                         rc = EMSGSIZE;
491                         goto read_fail;
492                 }
493         }
494
495         /*
496          * Handle the message based on its type
497          */
498         smp = (Scsp_if_msg *)buff;
499         switch(smp->si_type) {
500         case SCSP_CFG_RSP:
501                 if (smp->si_rc != SCSP_RSP_OK) {
502                         rc = EINVAL;
503                         goto read_fail;
504                 }
505                 break;
506         case SCSP_CACHE_IND:
507                 rc = atmarp_scsp_cache(aip, smp);
508                 break;
509         case SCSP_SOLICIT_IND:
510                 rc = atmarp_scsp_solicit(aip, smp);
511                 break;
512         case SCSP_UPDATE_IND:
513                 rc = atmarp_scsp_update_in(aip, smp);
514                 break;
515         case SCSP_UPDATE_RSP:
516                 /*
517                  * Ignore Update Responses
518                  */
519                 break;
520         default:
521                 atmarp_log(LOG_ERR, "Unexpected SCSP message received");
522                 return(EOPNOTSUPP);
523         }
524
525         UM_FREE(buff);
526         return(rc);
527
528 read_fail:
529         if (buff) {
530                 UM_FREE(buff);
531         }
532
533         /*
534          * Error on socket to SCSP--close the socket and set the state
535          * so that we know to retry when the cache timer fires.
536          */
537         atmarp_scsp_close(aip);
538
539         return(rc);
540 }
541
542
543 /*
544  * Send a message to SCSP
545  *
546  *
547  * Arguments:
548  *      aip     pointer to ATMARP interface to send message on
549  *      buff    pointer to message buffer
550  *      len     length of message
551  *
552  * Returns:
553  *      0       message sent
554  *      errno   reason for failure
555  *
556  */
557 int
558 atmarp_scsp_out(aip, buff, len)
559         Atmarp_intf     *aip;
560         char            *buff;
561         int             len;
562 {
563         int     rc;
564
565         /*
566          * Send the message to SCSP
567          */
568         rc = write(aip->ai_scsp_sock, buff, len);
569         if (rc == len)
570                 return(0);
571
572         /*
573          * Error on write--close the socket to SCSP, clean up and
574          * set the state so that we know to retry when the cache
575          * timer fires.
576          */
577         atmarp_scsp_close(aip);
578
579         /*
580          * Set the return code
581          */
582         if (rc < 0) {
583                 rc = errno;
584         } else {
585                 rc = EFAULT;
586         }
587
588         return(rc);
589 }
590
591
592 /*
593  * Set up a socket and connect to SCSP
594  *
595  * Arguments:
596  *      aip     pointer to interface block
597  *
598  * Returns:
599  *      0       success, ai_scsp_sock is set
600  *      errno   reason for failure
601  *
602  *
603  */
604 int
605 atmarp_scsp_connect(aip)
606         Atmarp_intf     *aip;
607 {
608         int             len, rc, sd;
609         char            *sn;
610         Scsp_if_msg     cfg_msg;
611
612         static struct sockaddr  local_addr = {
613 #if (defined(BSD) && (BSD >= 199103))
614                 sizeof(struct sockaddr),        /* sa_len */
615 #endif
616                 AF_UNIX,                        /* sa_family */
617                 ATMARP_SOCK_PREFIX              /* sa_data */
618         };
619         static struct sockaddr  scsp_addr = {
620 #if (defined(BSD) && (BSD >= 199103))
621                 sizeof(struct sockaddr),        /* sa_len */
622 #endif
623                 AF_UNIX,                        /* sa_family */
624                 SCSPD_SOCK_NAME                 /* sa_data */
625         };
626
627         /*
628          * Construct a name for the socket
629          */
630         strncpy(local_addr.sa_data, ATMARP_SOCK_PREFIX,
631                         sizeof(local_addr.sa_data));
632         (void)strncat(local_addr.sa_data, aip->ai_intf,
633                         sizeof(local_addr.sa_data));
634         sn = strdup(local_addr.sa_data);
635         if (!sn)
636                 atmarp_mem_err("atmarp_scsp_connect: strdup");
637
638         /*
639          * Clean up any old socket
640          */
641         rc = unlink(sn);
642         if (rc < 0 && errno != ENOENT)
643                 return(errno);
644
645         /*
646          * Open a socket to SCSP
647          */
648         sd = socket(PF_UNIX, SOCK_STREAM, 0);
649         if (sd == -1) {
650                 UM_FREE(sn);
651                 return(errno);
652         }
653         if (sd > atmarp_max_socket) {
654                 atmarp_max_socket = sd;
655         }
656
657         /*
658          * Set non-blocking I/O
659          */
660 #ifdef sun
661         rc = fcntl(sd, F_SETFL, FNBIO + FNDELAY);
662 #else
663         rc = fcntl(sd, F_SETFL, O_NONBLOCK);
664 #endif
665         if (rc == -1) {
666                 rc = errno;
667                 goto scsp_connect_fail;
668         }
669
670         /*
671          * Bind the local socket address
672          */
673         rc = bind(sd, &local_addr, sizeof(local_addr));
674         if (rc) {
675                 rc = errno;
676                 goto scsp_connect_fail;
677         }
678
679         /*
680          * Connect to SCSP
681          */
682         rc = connect(sd, &scsp_addr, sizeof(scsp_addr));
683         if (rc) {
684                 rc = errno;
685                 goto scsp_connect_fail;
686         }
687
688         /*
689          * Save socket information in interface control block
690          */
691         aip->ai_scsp_sock = sd;
692         aip->ai_scsp_sockname = sn;
693         aip->ai_state = AI_STATE_UP;
694
695         /*
696          * Send configuration information to SCSP
697          */
698         UM_ZERO(&cfg_msg, sizeof(cfg_msg));
699         cfg_msg.si_type = SCSP_CFG_REQ;
700         cfg_msg.si_proto = SCSP_PROTO_ATMARP;
701         strcpy(cfg_msg.si_cfg.atmarp_netif, aip->ai_intf);
702         len =sizeof(Scsp_if_msg_hdr) + strlen(aip->ai_intf) + 1;
703         cfg_msg.si_len = len;
704         rc = atmarp_scsp_out(aip, (char *)&cfg_msg, len);
705         if (rc) {
706                 return(rc);
707         }
708
709         return(0);
710
711 scsp_connect_fail:
712         (void)close(sd);
713         aip->ai_scsp_sock = -1;
714         UM_FREE(sn);
715         aip->ai_scsp_sockname = NULL;
716         aip->ai_state = AI_STATE_NULL;
717         return(rc);
718 }
719
720
721 /*
722  * Close a socket connection to SCSP
723  *
724  * Arguments:
725  *      aip     pointer to interface block for connection to be closed
726  *
727  * Returns:
728  *      none
729  *
730  *
731  */
732 void
733 atmarp_scsp_close(aip)
734         Atmarp_intf     *aip;
735 {
736         /*
737          * Close and unlink the SCSP socket
738          */
739         (void)close(aip->ai_scsp_sock);
740         aip->ai_scsp_sock = -1;
741         (void)unlink(aip->ai_scsp_sockname);
742         UM_FREE(aip->ai_scsp_sockname);
743         aip->ai_scsp_sockname = NULL;
744
745         aip->ai_state = AI_STATE_NULL;
746
747         return;
748 }
749
750
751 /*
752  * Disconnect an interface from SCSP
753  *
754  * Arguments:
755  *      aip     pointer to interface block for connection to be closed
756  *
757  * Returns:
758  *      0       success, ai_scsp_sock is set
759  *      errno   reason for failure
760  *
761  *
762  */
763 int
764 atmarp_scsp_disconnect(aip)
765         Atmarp_intf     *aip;
766 {
767         int     i;
768         Atmarp  *aap;
769
770         /*
771          * Close and unlink the SCSP socket
772          */
773         atmarp_scsp_close(aip);
774
775         /*
776          * Free the ATMARP cache associated with the interface
777          */
778         for (i = 0; i < ATMARP_HASHSIZ; i++) {
779                 for (aap = aip->ai_arptbl[i]; aap; aap = aap->aa_next) {
780                         UM_FREE(aap);
781                 }
782                 aip->ai_arptbl[i] = (Atmarp *)0;
783         }
784
785         return(0);
786 }