Parallelize in_ifaddrhead operation
[dragonfly.git] / sys / netproto / atm / ipatm / ipatm_output.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/sys/netatm/ipatm/ipatm_output.c,v 1.4.2.1 2000/06/02 22:39:08 archie Exp $
27  *      @(#) $DragonFly: src/sys/netproto/atm/ipatm/ipatm_output.c,v 1.8 2008/06/08 08:38:05 sephe Exp $
28  */
29
30 /*
31  * IP Over ATM Support
32  * -------------------
33  *
34  * Output IP packets across an ATM VCC
35  *
36  */
37
38 #include <netproto/atm/kern_include.h>
39
40 #include "ipatm_var.h"
41 #include "ipatm_serv.h"
42
43 /*
44  * Output an IP Packet
45  * 
46  * All IP packets output to an ATM interface will be directed here via
47  * the atm_ifoutput() function.  If there is an ATM VCC already setup for
48  * the destination IP address, then we'll just send the packet to that VCC.
49  * Otherwise we will have to setup a new VCC, ARPing for the corresponding
50  * destination ATM hardware address along the way.
51  *
52  * Arguments:
53  *      ifp     pointer to ifnet structure
54  *      m       pointer to packet buffer chain to be output
55  *      dst     pointer to packet's IP destination address
56  *
57  * Returns:
58  *      0       packet "output" was successful 
59  *      errno   output failed - reason indicated
60  *
61  */
62 int
63 ipatm_ifoutput(struct ifnet *ifp, KBuffer *m, struct sockaddr *dst)
64 {
65         struct ipvcc    *ivp;
66         int     err = 0;
67
68 #ifdef DIAGNOSTIC
69         if (ipatm_print) {
70                 atm_pdu_print(m, "ipatm_ifoutput");
71         }
72 #endif
73
74         /*
75          * See if we've already got an appropriate VCC
76          */
77         ivp = ipatm_iptovc((struct sockaddr_in *)dst, (struct atm_nif *)ifp);
78         if (ivp) {
79
80                 /*
81                  * Reset idle timer
82                  */
83                 ivp->iv_idle = 0;
84
85                 /*
86                  * Can we use this VCC now??
87                  */
88                 if ((ivp->iv_state == IPVCC_ACTIVE) && 
89                     (ivp->iv_flags & IVF_MAPOK)) {
90
91                         /*
92                          * OK, now send packet
93                          */
94                         err = atm_cm_cpcs_data(ivp->iv_conn, m);
95                         if (err) {
96                                 /*
97                                  * Output problem, drop packet
98                                  */
99                                 KB_FREEALL(m);
100                         }
101                 } else {
102
103                         /*
104                          * VCC is unavailable for data packets.  Queue packet
105                          * for now, but only maintain a queue length of one.
106                          */
107                         if (ivp->iv_queue)
108                                 KB_FREEALL(ivp->iv_queue);
109
110                         ivp->iv_queue = m;
111                 }
112         } else {
113                 struct in_ifaddr_container *iac;
114
115                 /*
116                  * No VCC to destination
117                  */
118
119                 /*
120                  * Is packet for our interface address?
121                  */
122                 TAILQ_FOREACH(iac, &in_ifaddrheads[mycpuid], ia_link) {
123                         struct in_ifaddr *ia = iac->ia;
124
125                         if (ia->ia_ifp != ifp)
126                                 continue;
127                         if (((struct sockaddr_in *)dst)->sin_addr.s_addr == 
128                                         IA_SIN(ia)->sin_addr.s_addr) {
129
130                                 /*
131                                  * It's for us - hand packet to loopback driver
132                                  */
133                                 if_simloop(ifp, m, dst->sa_family, 0);
134                                 goto done;
135                         }
136                 }
137
138                 /*
139                  * Is this a broadcast packet ??
140                  */
141                 if (in_broadcast(((struct sockaddr_in *)dst)->sin_addr, ifp)) {
142                         struct ip_nif   *inp;
143
144                         /*
145                          * If interface server exists and provides broadcast 
146                          * services, then let it deal with this packet
147                          */
148                         crit_enter();
149                         for (inp = ipatm_nif_head; inp; inp = inp->inf_next) {
150                                 if (inp->inf_nif == (struct atm_nif *)ifp)
151                                         break;
152                         }
153                         crit_exit();
154
155                         if ((inp == NULL) ||
156                             (inp->inf_serv == NULL) ||
157                             (inp->inf_serv->is_bcast_output == NULL)) {
158                                 KB_FREEALL(m);
159                                 err = EADDRNOTAVAIL;
160                                 goto done;
161                         }
162
163                         err = (*inp->inf_serv->is_bcast_output)(inp, m);
164                         goto done;
165                 }
166
167                 /*
168                  * How about a multicast packet ??
169                  */
170                 if (IN_MULTICAST(ntohl(SATOSIN(dst)->sin_addr.s_addr))) {
171                         /* 
172                          * Multicast isn't currently supported
173                          */
174                         KB_FREEALL(m);
175                         err = EADDRNOTAVAIL;
176                         goto done;
177                 }
178
179                 /*
180                  * Well, I guess we need to create an SVC to the destination
181                  */
182                 if ((err = ipatm_createsvc(ifp, AF_INET,
183                                 (caddr_t)&((struct sockaddr_in *)dst)->sin_addr,
184                                 &ivp)) == 0) {
185                         /*
186                          * SVC open is proceeding, queue packet 
187                          */
188                         ivp->iv_queue = m;
189
190                 } else {
191                         /*
192                          * SVC open failed, release buffers and return
193                          */
194                         KB_FREEALL(m);
195                 }
196         }
197
198 done:
199         return (err);
200 }
201