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