xref: /haiku/src/servers/net/AutoconfigLooper.cpp (revision adb0d19d561947362090081e81d90dde59142026)
1 /*
2  * Copyright 2006-2009, Haiku, Inc. All Rights Reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Axel Dörfler, axeld@pinc-software.de
7  */
8 
9 
10 #include "AutoconfigLooper.h"
11 
12 #include <errno.h>
13 #include <net/if_dl.h>
14 #include <net/if_media.h>
15 #include <net/if_types.h>
16 #include <stdio.h>
17 #include <sys/socket.h>
18 #include <sys/sockio.h>
19 
20 #include <net_notifications.h>
21 
22 #include "DHCPClient.h"
23 #include "NetServer.h"
24 
25 
26 static const uint32 kMsgReadyToRun = 'rdyr';
27 
28 
29 AutoconfigLooper::AutoconfigLooper(BMessenger target, const char* device)
30 	: BLooper(device),
31 	fTarget(target),
32 	fDevice(device),
33 	fCurrentClient(NULL)
34 {
35 	BMessage ready(kMsgReadyToRun);
36 	PostMessage(&ready);
37 }
38 
39 
40 AutoconfigLooper::~AutoconfigLooper()
41 {
42 }
43 
44 
45 void
46 AutoconfigLooper::_RemoveClient()
47 {
48 	if (fCurrentClient == NULL)
49 		return;
50 
51 	RemoveHandler(fCurrentClient);
52 	delete fCurrentClient;
53 	fCurrentClient = NULL;
54 }
55 
56 
57 void
58 AutoconfigLooper::_Configure()
59 {
60 	ifreq request;
61 	if (!prepare_request(request, fDevice.String()))
62 		return;
63 
64 	// set IFF_CONFIGURING flag on interface
65 
66 	int socket = ::socket(AF_INET, SOCK_DGRAM, 0);
67 	if (socket < 0)
68 		return;
69 
70 	if (ioctl(socket, SIOCGIFFLAGS, &request, sizeof(struct ifreq)) == 0) {
71 		request.ifr_flags |= IFF_CONFIGURING;
72 		ioctl(socket, SIOCSIFFLAGS, &request, sizeof(struct ifreq));
73 	}
74 
75 	// remove current handler
76 
77 	_RemoveClient();
78 
79 	// start with DHCP
80 
81 	fCurrentClient = new DHCPClient(fTarget, fDevice.String());
82 	AddHandler(fCurrentClient);
83 
84 	if (fCurrentClient->Initialize() == B_OK) {
85 		close(socket);
86 		return;
87 	}
88 
89 	_RemoveClient();
90 
91 	puts("DHCP failed miserably!");
92 
93 	// DHCP obviously didn't work out, take some default values for now
94 	// TODO: have a look at zeroconf
95 	// TODO: this could also be done add-on based
96 
97 	if (ioctl(socket, SIOCGIFFLAGS, &request, sizeof(struct ifreq)) == 0
98 		&& (request.ifr_flags & IFF_CONFIGURING) == 0) {
99 		// Someone else configured the interface in the mean time
100 		close(socket);
101 		return;
102 	}
103 
104 	close(socket);
105 
106 	BMessage interface(kMsgConfigureInterface);
107 	interface.AddString("device", fDevice.String());
108 	interface.AddBool("auto", true);
109 
110 	uint8 mac[6];
111 	uint8 last = 56;
112 	if (get_mac_address(fDevice.String(), mac) == B_OK) {
113 		// choose IP address depending on the MAC address, if available
114 		last = mac[0] ^ mac[1] ^ mac[2] ^ mac[3] ^ mac[4] ^ mac[5];
115 		if (last > 253)
116 			last = 253;
117 		else if (last == 0)
118 			last = 1;
119 	}
120 
121 	// IANA defined the default autoconfig network (for when a DHCP request
122 	// fails for some reason) as being 169.254.0.0/255.255.0.0. We are only
123 	// generating the last octet but we could also use the 2 last octets if
124 	// wanted.
125 	char string[64];
126 	snprintf(string, sizeof(string), "169.254.0.%u", last);
127 
128 	BMessage address;
129 	address.AddString("family", "inet");
130 	address.AddString("address", string);
131 	interface.AddMessage("address", &address);
132 
133 	fTarget.SendMessage(&interface);
134 }
135 
136 
137 void
138 AutoconfigLooper::_ReadyToRun()
139 {
140 	start_watching_network(B_WATCH_NETWORK_LINK_CHANGES, this);
141 	_Configure();
142 }
143 
144 
145 void
146 AutoconfigLooper::MessageReceived(BMessage* message)
147 {
148 	switch (message->what) {
149 		case kMsgReadyToRun:
150 			_ReadyToRun();
151 			break;
152 
153 		case B_NETWORK_MONITOR:
154 			const char* device;
155 			int32 opcode;
156 			int32 media;
157 			if (message->FindInt32("opcode", &opcode) != B_OK
158 				|| opcode != B_NETWORK_DEVICE_LINK_CHANGED
159 				|| message->FindString("device", &device) != B_OK
160 				|| fDevice != device
161 				|| message->FindInt32("media", &media) != B_OK)
162 				break;
163 
164 			if ((media & IFM_ACTIVE) != 0) {
165 				// Reconfigure the interface when we have a link again
166 				_Configure();
167 			}
168 			break;
169 
170 		default:
171 			BLooper::MessageReceived(message);
172 			break;
173 	}
174 }
175 
176