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