http://gimel.esc.cam.ac.uk/james/rpld/src/rpld-1.7.tar.gz
[rpld.git] / linux-nit.c
1 /*************************************************
2 *     rpld - an IBM style RIPL server            *
3 *************************************************/
4
5 /* Copyright (c) 1999,2000, James McKenzie.
6  *                      All rights reserved
7  * Copyright (c) 1998,2000, Christopher Lightfoot.
8  *                      All rights reserved
9  *
10  * By using this file, you agree to the terms and conditions set
11  * forth in the LICENCE file which can be found at the top level of
12  * the rpld distribution.
13  *
14  * IBM is a trademark of IBM corp.
15  *
16  */
17
18
19 static char rcsid[] = "$Id: linux-nit.c,v 1.2 2000/09/26 03:48:23 root Exp $";
20
21 /* 
22  * $Log: linux-nit.c,v $
23  * Revision 1.2  2000/09/26 03:48:23  root
24  * #
25  *
26  * Revision 1.1  2000/09/26 03:44:29  root
27  * #
28  *
29  * Revision 1.8  2000/07/16 14:24:16  root
30  * #
31  *
32  * Revision 1.7  2000/07/16 14:05:28  root
33  * #
34  *
35  * Revision 1.6  2000/07/16 13:18:10  root
36  * #
37  *
38  * Revision 1.1  2000/07/16 13:16:33  root
39  * #
40  *
41  * Revision 1.5  1999/09/13 11:17:35  root
42  * \#
43  *
44  * Revision 1.4  1999/09/11 16:52:15  root
45  * \#
46  *
47  * Revision 1.3  1999/09/11 16:34:50  root
48  * \#
49  *
50  * Revision 1.2  1999/09/10 22:06:43  root
51  * \#
52  *
53  * Revision 1.1  1999/09/10 17:32:26  root
54  * \#
55  *
56  * Revision 1.1  1999/05/17 21:53:33  root
57  * Initial revision
58  *
59  */
60
61 /*
62  * CAUTION THIS CODE COMES WITH NO WARARNTY IT IS COPYRIGHT 1999 BY
63  * James McKenzie All rights reserved. GPL applies
64  */
65
66
67 #include "project.h"
68 #include "nit.h"
69
70 #undef __GLIBC__
71 #include <linux/socket.h>
72 #include <linux/if_ether.h>
73 #include <linux/if_arp.h>
74 #include <linux/if_packet.h>
75 #include <linux/route.h>
76 #define __GLIBC__
77
78 struct nit
79 {
80   int fd;
81   int index;
82   unsigned char mac[ETH_ALEN];
83   char *name;
84 };
85
86
87 static void
88 get_hwaddr (unsigned char *name, unsigned char *addr)
89 {
90   struct ifreq ifr;
91   int fd = socket (AF_INET, SOCK_DGRAM, 0);
92
93   if (fd < 0)
94     {
95       syslog (LOG_ERR, "socket:%m");
96       return;
97     }
98   bcopy (name, &ifr.ifr_name, sizeof (ifr.ifr_name));
99
100
101   /* find my own hardware address */
102   if (ioctl (fd, SIOCGIFHWADDR, &ifr) < 0)
103     {
104       close (fd);
105       syslog (LOG_ERR, "ioctl(SIOCGIFHWADDR):%m");
106       exit (-1);
107     }
108   close (fd);
109
110   bcopy (&ifr.ifr_hwaddr.sa_data, addr, ETH_ALEN);
111
112 }
113
114 void
115 nit_close (struct nit *n)
116 {
117   if (!n)
118     return;
119
120   if (n->name)
121     free (n->name);
122   close (n->fd);
123
124 /*FIXME: we should demulticast at this point but we need to have some 
125  *mechanism for detecting if anyone else wants it
126  */
127
128   free (n);
129 }
130
131 struct nit *
132 nit_open (char *name)
133 {
134   struct nit *n;
135   struct sockaddr_ll sll;
136   struct ifreq ifr;
137   int fd;
138
139   syslog (LOG_ERR, "RPLD 802.2 LINUX-NIT support %s", rcsid);
140
141   fd = socket (PF_PACKET, SOCK_DGRAM, 0);
142
143   if (fd < 0)
144     {
145       syslog (LOG_ERR, "socket:%m");
146       return (NULL);
147     }
148
149   ifr.ifr_ifindex = 0;
150   if (name)
151     {
152
153       strcpy (ifr.ifr_name, name);
154
155       if (ioctl (fd, SIOCGIFINDEX, &ifr) < 0)
156         {
157           syslog (LOG_ERR, "SIOCGIFINDEX on %s: %m\n", name);
158           close (fd);
159           return NULL;
160         }
161
162     }
163
164
165
166   bzero (&sll, sizeof (sll));
167   sll.sll_family = AF_PACKET;
168   sll.sll_protocol = htons (ETH_P_802_2);
169   sll.sll_ifindex = ifr.ifr_ifindex;
170
171   if (bind (fd, &sll, sizeof (sll)) < 0)
172     {
173       close (fd);
174       syslog (LOG_ERR, "bind:%m");
175       return (NULL);
176     }
177
178
179   n = (struct nit *) malloc (sizeof (struct nit));
180
181   n->index = ifr.ifr_ifindex;
182
183   n->fd = fd;
184
185   get_hwaddr (name, n->mac);
186
187   if (name)
188     {
189       n->name = strdup (name);
190     }
191   else
192     {
193       n->name = NULL;
194     }
195
196   return (n);
197 }
198
199 unsigned char *
200 nit_mac (struct nit *n)
201 {
202   return (n->mac);
203 }
204
205 int
206 nit_send (struct nit *n, unsigned char *frame, int len, unsigned char *to)
207 {
208   struct sockaddr_ll sll;
209   struct msghdr msg;
210   struct iovec iov;
211
212   bzero (&msg, sizeof (msg));
213   bzero (&sll, sizeof (sll));
214
215
216   sll.sll_family = AF_PACKET;
217 /* This is the most horrible hack you are ever likely to see*/
218 /* if you don't understand what's happening in the line below*/
219 /* be very very happy */
220   sll.sll_protocol = htons (len);
221   sll.sll_ifindex = n->index;
222   sll.sll_hatype = ARPHRD_ETHER;
223   sll.sll_pkttype = PACKET_OUTGOING;
224   sll.sll_halen = ETH_ALEN;
225   bcopy (to, &sll.sll_addr[0], ETH_ALEN);
226
227   if (sendto (n->fd, frame, len, 0, &sll, sizeof (sll)) < 0)
228     {
229       syslog (LOG_ERR, "sendto: %m");
230       return -1;
231     }
232
233   return len;
234
235 }
236
237
238 int
239 nit_multicast (struct nit *n, unsigned char *mcaddr)
240 {
241   struct ifreq ifr;
242   int fd = socket (AF_INET, SOCK_DGRAM, 0);
243
244   strncpy (ifr.ifr_name, n->name, sizeof (ifr.ifr_name));
245
246   ifr.ifr_hwaddr.sa_family = AF_UNSPEC;
247
248   bcopy (mcaddr, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
249
250   if (ioctl (fd, SIOCADDMULTI, &ifr) < 0)
251     {
252       close (fd);
253       syslog (LOG_ERR, "ioctl(SIOCADDMULTI):%m");
254       return -1;
255     }
256
257   return 0;
258 }
259
260 int
261 nit_recv (struct nit *n, unsigned char *buf, int len, unsigned char *ufrom,
262           struct timeval *tv)
263 {
264   fd_set rfds;
265   int ret;
266   struct sockaddr_ll from;
267   int fromlen = sizeof (from);
268
269
270   FD_ZERO (&rfds);
271   FD_SET (n->fd, &rfds);
272
273   ret = select ((n->fd) + 1, &rfds, NULL, NULL, tv);
274
275   if (ret < 0)
276     return (ret);
277
278   if (FD_ISSET ((n->fd), &rfds))
279     {
280       ret =
281         recvfrom (n->fd, buf, len, 0, (struct sockaddr *) &from, &fromlen);
282       if (ufrom)
283         bcopy (from.sll_addr, ufrom, ETH_ALEN);
284
285       return (ret);
286     }
287   else
288     {
289       return (0);
290     }
291
292 }