1 /*
2  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /*
7  * Copyright (c) 2003-2004, Jouni Malinen <jkmaline@cc.hut.fi>
8  * Sun elects to license this software under the BSD license.
9  * See README for more details.
10  */
11 
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <unistd.h>
16 #include <libdlpi.h>
17 #include <sys/ethernet.h>
18 #include <netinet/in.h>
19 
20 #include "wpa_impl.h"
21 #include "eloop.h"
22 #include "l2_packet.h"
23 
24 static int
link_init(struct l2_packet_data * l2)25 link_init(struct l2_packet_data *l2)
26 {
27 	int retval;
28 	uint8_t paddr[DLPI_PHYSADDR_MAX];
29 	size_t paddrlen = sizeof (paddr);
30 
31 	retval = dlpi_bind(l2->dh, DLPI_ANY_SAP, NULL);
32 	if (retval != DLPI_SUCCESS) {
33 		wpa_printf(MSG_ERROR, "cannot bind on %s: %s",
34 		    l2->ifname, dlpi_strerror(retval));
35 		return (-1);
36 	}
37 
38 	retval = dlpi_promiscon(l2->dh, DL_PROMISC_SAP);
39 	if (retval != DLPI_SUCCESS) {
40 		wpa_printf(MSG_ERROR, "cannot enable promiscous"
41 		    " mode (SAP) on %s: %s",
42 		    l2->ifname, dlpi_strerror(retval));
43 		return (-1);
44 	}
45 
46 	retval = dlpi_get_physaddr(l2->dh, DL_CURR_PHYS_ADDR, paddr, &paddrlen);
47 	if (retval != DLPI_SUCCESS) {
48 		wpa_printf(MSG_ERROR, "cannot get physical address for %s: %s",
49 		    l2->ifname, dlpi_strerror(retval));
50 		return (-1);
51 	}
52 	if (paddrlen != sizeof (l2->own_addr)) {
53 		wpa_printf(MSG_ERROR, "physical address for %s is not %d bytes",
54 		    l2->ifname, sizeof (l2->own_addr));
55 		return (-1);
56 	}
57 	(void) memcpy(l2->own_addr, paddr, sizeof (l2->own_addr));
58 
59 	return (0);
60 }
61 
62 /*
63  * layer2 packet handling.
64  */
65 int
l2_packet_get_own_addr(struct l2_packet_data * l2,uint8_t * addr)66 l2_packet_get_own_addr(struct l2_packet_data *l2, uint8_t *addr)
67 {
68 	(void) memcpy(addr, l2->own_addr, sizeof (l2->own_addr));
69 	return (0);
70 }
71 
72 int
l2_packet_send(struct l2_packet_data * l2,uint8_t * buf,size_t buflen)73 l2_packet_send(struct l2_packet_data *l2, uint8_t *buf, size_t buflen)
74 {
75 	int retval;
76 
77 	retval = dlpi_send(l2->dh, NULL, 0, buf, buflen, NULL);
78 	if (retval != DLPI_SUCCESS) {
79 		wpa_printf(MSG_ERROR, "l2_packet_send: cannot send "
80 		    "message on %s: %s", l2->ifname, dlpi_strerror(retval));
81 		return (-1);
82 	}
83 	return (0);
84 }
85 
86 /* ARGSUSED */
87 static void
l2_packet_receive(int fd,void * eloop_ctx,void * sock_ctx)88 l2_packet_receive(int fd, void *eloop_ctx, void *sock_ctx)
89 {
90 	struct l2_packet_data *l2 = eloop_ctx;
91 	uint64_t buf[IEEE80211_MTU_MAX / sizeof (uint64_t)];
92 	size_t buflen = sizeof (buf);
93 	struct l2_ethhdr *ethhdr;
94 	int retval;
95 
96 	retval = dlpi_recv(l2->dh, NULL, NULL, buf, &buflen, 0, NULL);
97 	if (retval != DLPI_SUCCESS) {
98 		wpa_printf(MSG_ERROR, "l2_packet_receive: cannot receive "
99 		    "message on %s: %s", l2->ifname, dlpi_strerror(retval));
100 		return;
101 	}
102 
103 	ethhdr = (struct l2_ethhdr *)buf;
104 	if (buflen < sizeof (*ethhdr) ||
105 	    (ntohs(ethhdr->h_proto) != ETHERTYPE_EAPOL &&
106 	    ntohs(ethhdr->h_proto) != ETHERTYPE_RSN_PREAUTH))
107 		return;
108 
109 	l2->rx_callback(l2->rx_callback_ctx, ethhdr->h_source,
110 	    (unsigned char *)(ethhdr + 1), buflen - sizeof (*ethhdr));
111 }
112 
113 /* ARGSUSED */
114 struct l2_packet_data *
l2_packet_init(const char * ifname,unsigned short protocol,void (* rx_callback)(void *,unsigned char *,unsigned char *,size_t),void * rx_callback_ctx)115 l2_packet_init(const char *ifname, unsigned short protocol,
116 	void (*rx_callback)(void *, unsigned char *, unsigned char *, size_t),
117 	void *rx_callback_ctx)
118 {
119 	int retval;
120 	struct l2_packet_data *l2;
121 
122 	l2 = calloc(1, sizeof (struct l2_packet_data));
123 	if (l2 == NULL)
124 		return (NULL);
125 
126 	(void) strlcpy(l2->ifname, ifname, sizeof (l2->ifname));
127 	l2->rx_callback = rx_callback;
128 	l2->rx_callback_ctx = rx_callback_ctx;
129 
130 	retval = dlpi_open(l2->ifname, &l2->dh, DLPI_RAW);
131 	if (retval != DLPI_SUCCESS) {
132 		wpa_printf(MSG_ERROR, "unable to open DLPI link %s: %s",
133 		    l2->ifname, dlpi_strerror(retval));
134 		free(l2);
135 		return (NULL);
136 	}
137 
138 	/* NOTE: link_init() sets l2->own_addr */
139 	if (link_init(l2) < 0) {
140 		dlpi_close(l2->dh);
141 		free(l2);
142 		return (NULL);
143 	}
144 
145 	(void) eloop_register_read_sock(dlpi_fd(l2->dh), l2_packet_receive, l2,
146 	    NULL);
147 
148 	return (l2);
149 }
150 
151 void
l2_packet_deinit(struct l2_packet_data * l2)152 l2_packet_deinit(struct l2_packet_data *l2)
153 {
154 	if (l2 == NULL)
155 		return;
156 
157 	eloop_unregister_read_sock(dlpi_fd(l2->dh));
158 	dlpi_close(l2->dh);
159 	free(l2);
160 }
161