3 * Copyright (c) 2000-2003 Intel Corporation
4 * Copyright (c) 2006-2007 Sony Corporation
5 * Copyright (c) 2008-2009 Atheros Communications
6 * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
8 * See wps_upnp.c for more details on licensing and code history.
14 #include <sys/ioctl.h>
15 #include <net/route.h>
22 #include "wps_upnp_i.h"
24 #define UPNP_CACHE_SEC (UPNP_CACHE_SEC_MIN + 1) /* cache time we use */
25 #define UPNP_CACHE_SEC_MIN 1800 /* min cachable time per UPnP standard */
26 #define UPNP_ADVERTISE_REPEAT 2 /* no more than 3 */
27 #define MULTICAST_MAX_READ 1600 /* max bytes we'll read for UPD request */
28 #define MAX_MSEARCH 20 /* max simultaneous M-SEARCH replies ongoing */
29 #define SSDP_TARGET "239.0.0.0"
30 #define SSDP_NETMASK "255.0.0.0"
33 /* Check tokens for equality, where tokens consist of letters, digits,
34 * underscore and hyphen, and are matched case insensitive.
36 static int token_eq(const char *s1
, const char *s2
)
45 if (isalpha(c1
) && isupper(c1
))
47 if (isalpha(c2
) && isupper(c2
))
49 end1
= !(isalnum(c1
) || c1
== '_' || c1
== '-');
50 end2
= !(isalnum(c2
) || c2
== '_' || c2
== '-');
51 if (end1
|| end2
|| c1
!= c2
)
54 return end1
&& end2
; /* reached end of both words? */
58 /* Return length of token (see above for definition of token) */
59 static int token_length(const char *s
)
61 const char *begin
= s
;
64 int end
= !(isalnum(c
) || c
== '_' || c
== '-');
72 /* return length of interword separation.
73 * This accepts only spaces/tabs and thus will not traverse a line
76 static int word_separation_length(const char *s
)
78 const char *begin
= s
;
81 if (c
== ' ' || c
== '\t')
89 /* No. of chars through (including) end of line */
90 static int line_length(const char *l
)
93 while (*lp
&& *lp
!= '\n')
101 /* No. of chars excluding trailing whitespace */
102 static int line_length_stripped(const char *l
)
104 const char *lp
= l
+ line_length(l
);
105 while (lp
> l
&& !isgraph(lp
[-1]))
111 static int str_starts(const char *str
, const char *start
)
113 return os_strncmp(str
, start
, os_strlen(start
)) == 0;
117 /***************************************************************************
119 * These are multicast to the world to tell them we are here.
120 * The individual packets are spread out in time to limit loss,
121 * and then after a much longer period of time the whole sequence
122 * is repeated again (for NOTIFYs only).
123 **************************************************************************/
126 * next_advertisement - Build next message and advance the state machine
127 * @a: Advertisement state
128 * @islast: Buffer for indicating whether this is the last message (= 1)
129 * Returns: The new message (caller is responsible for freeing this)
131 * Note: next_advertisement is shared code with msearchreply_* functions
133 static struct wpabuf
*
134 next_advertisement(struct advertisement_state_machine
*a
, int *islast
)
138 char uuid_string
[80];
141 uuid_bin2str(a
->sm
->wps
->uuid
, uuid_string
, sizeof(uuid_string
));
142 msg
= wpabuf_alloc(800); /* more than big enough */
149 wpabuf_put_str(msg
, "NOTIFY * HTTP/1.1\r\n");
150 wpabuf_printf(msg
, "HOST: %s:%d\r\n",
151 UPNP_MULTICAST_ADDRESS
, UPNP_MULTICAST_PORT
);
152 wpabuf_printf(msg
, "CACHE-CONTROL: max-age=%d\r\n",
154 wpabuf_printf(msg
, "NTS: %s\r\n",
155 (a
->type
== ADVERTISE_UP
?
156 "ssdp:alive" : "ssdp:byebye"));
160 wpabuf_put_str(msg
, "HTTP/1.1 200 OK\r\n");
161 wpabuf_printf(msg
, "CACHE-CONTROL: max-age=%d\r\n",
164 wpabuf_put_str(msg
, "DATE: ");
166 wpabuf_put_str(msg
, "\r\n");
168 wpabuf_put_str(msg
, "EXT:\r\n");
172 if (a
->type
!= ADVERTISE_DOWN
) {
173 /* Where others may get our XML files from */
174 wpabuf_printf(msg
, "LOCATION: http://%s:%d/%s\r\n",
175 a
->sm
->ip_addr_text
, a
->sm
->web_port
,
176 UPNP_WPS_DEVICE_XML_FILE
);
179 /* The SERVER line has three comma-separated fields:
180 * operating system / version
182 * software package / version
183 * However, only the UPnP version is really required, the
184 * others can be place holders... for security reasons
185 * it is better to NOT provide extra information.
187 wpabuf_put_str(msg
, "SERVER: Unspecified, UPnP/1.0, Unspecified\r\n");
189 switch (a
->state
/ UPNP_ADVERTISE_REPEAT
) {
191 wpabuf_printf(msg
, "%s: upnp:rootdevice\r\n", NTString
);
192 wpabuf_printf(msg
, "USN: uuid:%s::upnp:rootdevice\r\n",
196 wpabuf_printf(msg
, "%s: uuid:%s\r\n", NTString
, uuid_string
);
197 wpabuf_printf(msg
, "USN: uuid:%s\r\n", uuid_string
);
200 wpabuf_printf(msg
, "%s: urn:schemas-wifialliance-org:device:"
201 "WFADevice:1\r\n", NTString
);
202 wpabuf_printf(msg
, "USN: uuid:%s::urn:schemas-wifialliance-"
203 "org:device:WFADevice:1\r\n", uuid_string
);
206 wpabuf_printf(msg
, "%s: urn:schemas-wifialliance-org:service:"
207 "WFAWLANConfig:1\r\n", NTString
);
208 wpabuf_printf(msg
, "USN: uuid:%s::urn:schemas-wifialliance-"
209 "org:service:WFAWLANConfig:1\r\n", uuid_string
);
212 wpabuf_put_str(msg
, "\r\n");
214 if (a
->state
+ 1 >= 4 * UPNP_ADVERTISE_REPEAT
)
225 static void advertisement_state_machine_handler(void *eloop_data
,
230 * advertisement_state_machine_stop - Stop SSDP advertisements
231 * @sm: WPS UPnP state machine from upnp_wps_device_init()
232 * @send_byebye: Send byebye advertisement messages immediately
234 void advertisement_state_machine_stop(struct upnp_wps_device_sm
*sm
,
237 struct advertisement_state_machine
*a
= &sm
->advertisement
;
240 struct sockaddr_in dest
;
242 eloop_cancel_timeout(advertisement_state_machine_handler
, NULL
, sm
);
243 if (!send_byebye
|| sm
->multicast_sd
< 0)
246 a
->type
= ADVERTISE_DOWN
;
250 os_memset(&dest
, 0, sizeof(dest
));
251 dest
.sin_family
= AF_INET
;
252 dest
.sin_addr
.s_addr
= inet_addr(UPNP_MULTICAST_ADDRESS
);
253 dest
.sin_port
= htons(UPNP_MULTICAST_PORT
);
256 msg
= next_advertisement(a
, &islast
);
259 if (sendto(sm
->multicast_sd
, wpabuf_head(msg
), wpabuf_len(msg
),
260 0, (struct sockaddr
*) &dest
, sizeof(dest
)) < 0) {
261 wpa_printf(MSG_INFO
, "WPS UPnP: Advertisement sendto "
262 "failed: %d (%s)", errno
, strerror(errno
));
270 static void advertisement_state_machine_handler(void *eloop_data
,
273 struct upnp_wps_device_sm
*sm
= user_ctx
;
274 struct advertisement_state_machine
*a
= &sm
->advertisement
;
276 int next_timeout_msec
= 100;
277 int next_timeout_sec
= 0;
278 struct sockaddr_in dest
;
282 * Each is sent twice (in case lost) w/ 100 msec delay between;
283 * spec says no more than 3 times.
284 * One pair for rootdevice, one pair for uuid, and a pair each for
285 * each of the two urns.
286 * The entire sequence must be repeated before cache control timeout
287 * (which is min 1800 seconds),
288 * recommend random portion of half of the advertised cache control age
289 * to ensure against loss... perhaps 1800/4 + rand*1800/4 ?
290 * Delay random interval < 100 msec prior to initial sending.
294 wpa_printf(MSG_MSGDUMP
, "WPS UPnP: Advertisement state=%d", a
->state
);
295 msg
= next_advertisement(a
, &islast
);
299 os_memset(&dest
, 0, sizeof(dest
));
300 dest
.sin_family
= AF_INET
;
301 dest
.sin_addr
.s_addr
= inet_addr(UPNP_MULTICAST_ADDRESS
);
302 dest
.sin_port
= htons(UPNP_MULTICAST_PORT
);
304 if (sendto(sm
->multicast_sd
, wpabuf_head(msg
), wpabuf_len(msg
), 0,
305 (struct sockaddr
*) &dest
, sizeof(dest
)) == -1) {
306 wpa_printf(MSG_ERROR
, "WPS UPnP: Advertisement sendto failed:"
307 "%d (%s)", errno
, strerror(errno
));
308 next_timeout_msec
= 0;
309 next_timeout_sec
= 10; /* ... later */
311 a
->state
= 0; /* wrap around */
312 if (a
->type
== ADVERTISE_DOWN
) {
313 wpa_printf(MSG_DEBUG
, "WPS UPnP: ADVERTISE_DOWN->UP");
314 a
->type
= ADVERTISE_UP
;
315 /* do it all over again right away */
319 * Start over again after a long timeout
322 next_timeout_msec
= 0;
323 os_get_random((void *) &r
, sizeof(r
));
324 next_timeout_sec
= UPNP_CACHE_SEC
/ 4 +
325 (((UPNP_CACHE_SEC
/ 4) * r
) >> 16);
326 sm
->advertise_count
++;
327 wpa_printf(MSG_DEBUG
, "WPS UPnP: ADVERTISE_UP (#%u); "
329 sm
->advertise_count
, next_timeout_sec
);
337 eloop_register_timeout(next_timeout_sec
, next_timeout_msec
,
338 advertisement_state_machine_handler
, NULL
, sm
);
343 * advertisement_state_machine_start - Start SSDP advertisements
344 * @sm: WPS UPnP state machine from upnp_wps_device_init()
345 * Returns: 0 on success, -1 on failure
347 int advertisement_state_machine_start(struct upnp_wps_device_sm
*sm
)
349 struct advertisement_state_machine
*a
= &sm
->advertisement
;
350 int next_timeout_msec
;
352 advertisement_state_machine_stop(sm
, 0);
355 * Start out advertising down, this automatically switches
356 * to advertising up which signals our restart.
358 a
->type
= ADVERTISE_DOWN
;
361 /* (other fields not used here) */
363 /* First timeout should be random interval < 100 msec */
364 next_timeout_msec
= (100 * (os_random() & 0xFF)) >> 8;
365 return eloop_register_timeout(0, next_timeout_msec
,
366 advertisement_state_machine_handler
,
371 /***************************************************************************
373 * These are very similar to the multicast advertisements, with some
374 * small changes in data content; and they are sent (UDP) to a specific
375 * unicast address instead of multicast.
376 * They are sent in response to a UDP M-SEARCH packet.
377 **************************************************************************/
379 static void msearchreply_state_machine_handler(void *eloop_data
,
384 * msearchreply_state_machine_stop - Stop M-SEARCH reply state machine
385 * @a: Selected advertisement/reply state
387 void msearchreply_state_machine_stop(struct advertisement_state_machine
*a
)
389 struct upnp_wps_device_sm
*sm
= a
->sm
;
390 wpa_printf(MSG_DEBUG
, "WPS UPnP: M-SEARCH stop");
392 sm
->msearch_replies
= NULL
;
394 if (sm
->msearch_replies
== a
)
395 sm
->msearch_replies
= a
->next
;
396 a
->next
->prev
= a
->prev
;
397 a
->prev
->next
= a
->next
;
400 sm
->n_msearch_replies
--;
404 static void msearchreply_state_machine_handler(void *eloop_data
,
407 struct advertisement_state_machine
*a
= user_ctx
;
408 struct upnp_wps_device_sm
*sm
= a
->sm
;
410 int next_timeout_msec
= 100;
411 int next_timeout_sec
= 0;
415 * Each response is sent twice (in case lost) w/ 100 msec delay
416 * between; spec says no more than 3 times.
417 * One pair for rootdevice, one pair for uuid, and a pair each for
418 * each of the two urns.
421 /* TODO: should only send the requested response types */
423 wpa_printf(MSG_MSGDUMP
, "WPS UPnP: M-SEARCH reply state=%d (%s:%d)",
424 a
->state
, inet_ntoa(a
->client
.sin_addr
),
425 ntohs(a
->client
.sin_port
));
426 msg
= next_advertisement(a
, &islast
);
431 * Send it on the multicast socket to avoid having to set up another
434 if (sendto(sm
->multicast_sd
, wpabuf_head(msg
), wpabuf_len(msg
), 0,
435 (struct sockaddr
*) &a
->client
, sizeof(a
->client
)) < 0) {
436 wpa_printf(MSG_DEBUG
, "WPS UPnP: M-SEARCH reply sendto "
437 "errno %d (%s) for %s:%d",
438 errno
, strerror(errno
),
439 inet_ntoa(a
->client
.sin_addr
),
440 ntohs(a
->client
.sin_port
));
441 /* Ignore error and hope for the best */
445 wpa_printf(MSG_DEBUG
, "WPS UPnP: M-SEARCH reply done");
446 msearchreply_state_machine_stop(a
);
451 wpa_printf(MSG_MSGDUMP
, "WPS UPnP: M-SEARCH reply in %d.%03d sec",
452 next_timeout_sec
, next_timeout_msec
);
453 eloop_register_timeout(next_timeout_sec
, next_timeout_msec
,
454 msearchreply_state_machine_handler
, sm
, a
);
459 * msearchreply_state_machine_start - Reply to M-SEARCH discovery request
460 * @sm: WPS UPnP state machine from upnp_wps_device_init()
461 * @client: Client address
462 * @mx: Maximum delay in seconds
464 * Use TTL of 4 (this was done when socket set up).
465 * A response should be given in randomized portion of min(MX,120) seconds
467 * UPnP-arch-DeviceArchitecture, 1.2.3:
468 * To be found, a device must send a UDP response to the source IP address and
469 * port that sent the request to the multicast channel. Devices respond if the
470 * ST header of the M-SEARCH request is "ssdp:all", "upnp:rootdevice", "uuid:"
471 * followed by a UUID that exactly matches one advertised by the device.
473 static void msearchreply_state_machine_start(struct upnp_wps_device_sm
*sm
,
474 struct sockaddr_in
*client
,
477 struct advertisement_state_machine
*a
;
478 int next_timeout_sec
;
479 int next_timeout_msec
;
481 wpa_printf(MSG_DEBUG
, "WPS UPnP: M-SEARCH reply start (%d "
482 "outstanding)", sm
->n_msearch_replies
);
483 if (sm
->n_msearch_replies
>= MAX_MSEARCH
) {
484 wpa_printf(MSG_INFO
, "WPS UPnP: Too many outstanding "
489 a
= os_zalloc(sizeof(*a
));
492 a
->type
= MSEARCH_REPLY
;
495 os_memcpy(&a
->client
, client
, sizeof(*client
));
496 /* Wait time depending on MX value */
497 next_timeout_msec
= (1000 * mx
* (os_random() & 0xFF)) >> 8;
498 next_timeout_sec
= next_timeout_msec
/ 1000;
499 next_timeout_msec
= next_timeout_msec
% 1000;
500 if (eloop_register_timeout(next_timeout_sec
, next_timeout_msec
,
501 msearchreply_state_machine_handler
, sm
,
503 /* No way to recover (from malloc failure) */
506 /* Remember for future cleanup */
507 if (sm
->msearch_replies
) {
508 a
->next
= sm
->msearch_replies
;
509 a
->prev
= a
->next
->prev
;
513 sm
->msearch_replies
= a
->next
= a
->prev
= a
;
515 sm
->n_msearch_replies
++;
519 wpa_printf(MSG_INFO
, "WPS UPnP: M-SEARCH reply failure!");
520 eloop_cancel_timeout(msearchreply_state_machine_handler
, sm
, a
);
526 * ssdp_parse_msearch - Process a received M-SEARCH
527 * @sm: WPS UPnP state machine from upnp_wps_device_init()
528 * @client: Client address
529 * @data: NULL terminated M-SEARCH message
531 * Given that we have received a header w/ M-SEARCH, act upon it
533 * Format of M-SEARCH (case insensitive!):
535 * First line must be:
536 * M-SEARCH * HTTP/1.1
537 * Other lines in arbitrary order:
538 * HOST:239.255.255.250:1900
539 * ST:<varies -- must match>
540 * MAN:"ssdp:discover"
543 * It should be noted that when Microsoft Vista is still learning its IP
544 * address, it sends out host lines like: HOST:[FF02::C]:1900
546 static void ssdp_parse_msearch(struct upnp_wps_device_sm
*sm
,
547 struct sockaddr_in
*client
, const char *data
)
549 const char *start
= data
;
552 int got_st
= 0, st_match
= 0;
558 * Skip first line M-SEARCH * HTTP/1.1
559 * (perhaps we should check remainder of the line for syntax)
561 data
+= line_length(data
);
563 /* Parse remaining lines */
564 for (; *data
!= '\0'; data
+= line_length(data
)) {
565 end
= data
+ line_length_stripped(data
);
566 if (token_eq(data
, "host")) {
567 /* The host line indicates who the packet
568 * is addressed to... but do we really care?
569 * Note that Microsoft sometimes does funny
570 * stuff with the HOST: line.
573 data
+= token_length(data
);
574 data
+= word_separation_length(data
);
578 data
+= word_separation_length(data
);
579 /* UPNP_MULTICAST_ADDRESS */
580 if (!str_starts(data
, "239.255.255.250"))
582 data
+= os_strlen("239.255.255.250");
584 if (!str_starts(data
, ":1900"))
587 #endif /* could be */
590 } else if (token_eq(data
, "st")) {
591 /* There are a number of forms; we look
592 * for one that matches our case.
595 data
+= token_length(data
);
596 data
+= word_separation_length(data
);
600 data
+= word_separation_length(data
);
601 if (str_starts(data
, "ssdp:all")) {
605 if (str_starts(data
, "upnp:rootdevice")) {
609 if (str_starts(data
, "uuid:")) {
610 char uuid_string
[80];
611 data
+= os_strlen("uuid:");
612 uuid_bin2str(sm
->wps
->uuid
, uuid_string
,
613 sizeof(uuid_string
));
614 if (str_starts(data
, uuid_string
))
619 /* FIX: should we really reply to IGD string? */
620 if (str_starts(data
, "urn:schemas-upnp-org:device:"
621 "InternetGatewayDevice:1")) {
626 if (str_starts(data
, "urn:schemas-wifialliance-org:"
627 "service:WFAWLANConfig:1")) {
631 if (str_starts(data
, "urn:schemas-wifialliance-org:"
632 "device:WFADevice:1")) {
637 } else if (token_eq(data
, "man")) {
638 data
+= token_length(data
);
639 data
+= word_separation_length(data
);
643 data
+= word_separation_length(data
);
644 if (!str_starts(data
, "\"ssdp:discover\"")) {
645 wpa_printf(MSG_DEBUG
, "WPS UPnP: Unexpected "
646 "M-SEARCH man-field");
651 } else if (token_eq(data
, "mx")) {
652 data
+= token_length(data
);
653 data
+= word_separation_length(data
);
657 data
+= word_separation_length(data
);
662 /* ignore anything else */
664 if (!got_host
|| !got_st
|| !got_man
|| !got_mx
|| mx
< 0) {
665 wpa_printf(MSG_DEBUG
, "WPS UPnP: Invalid M-SEARCH: %d %d %d "
666 "%d mx=%d", got_host
, got_st
, got_man
, got_mx
, mx
);
670 wpa_printf(MSG_DEBUG
, "WPS UPnP: Ignored M-SEARCH (no ST "
675 mx
= 120; /* UPnP-arch-DeviceArchitecture, 1.2.3 */
676 msearchreply_state_machine_start(sm
, client
, mx
);
680 wpa_printf(MSG_INFO
, "WPS UPnP: Failed to parse M-SEARCH");
681 wpa_printf(MSG_MSGDUMP
, "WPS UPnP: M-SEARCH data:\n%s", start
);
685 /* Listening for (UDP) discovery (M-SEARCH) packets */
688 * ssdp_listener_stop - Stop SSDP listered
689 * @sm: WPS UPnP state machine from upnp_wps_device_init()
691 * This function stops the SSDP listerner that was started by calling
692 * ssdp_listener_start().
694 void ssdp_listener_stop(struct upnp_wps_device_sm
*sm
)
696 if (sm
->ssdp_sd_registered
) {
697 eloop_unregister_sock(sm
->ssdp_sd
, EVENT_TYPE_READ
);
698 sm
->ssdp_sd_registered
= 0;
701 if (sm
->ssdp_sd
!= -1) {
706 eloop_cancel_timeout(msearchreply_state_machine_handler
, sm
,
711 static void ssdp_listener_handler(int sd
, void *eloop_ctx
, void *sock_ctx
)
713 struct upnp_wps_device_sm
*sm
= sock_ctx
;
714 struct sockaddr_in addr
; /* client address */
717 char buf
[MULTICAST_MAX_READ
], *pos
;
719 addr_len
= sizeof(addr
);
720 nread
= recvfrom(sm
->ssdp_sd
, buf
, sizeof(buf
) - 1, 0,
721 (struct sockaddr
*) &addr
, &addr_len
);
724 buf
[nread
] = '\0'; /* need null termination for algorithm */
726 if (str_starts(buf
, "NOTIFY ")) {
728 * Silently ignore NOTIFYs to avoid filling debug log with
734 pos
= os_strchr(buf
, '\n');
737 wpa_printf(MSG_MSGDUMP
, "WPS UPnP: Received SSDP packet from %s:%d: "
738 "%s", inet_ntoa(addr
.sin_addr
), ntohs(addr
.sin_port
), buf
);
742 /* Parse first line */
743 if (os_strncasecmp(buf
, "M-SEARCH", os_strlen("M-SEARCH")) == 0 &&
744 !isgraph(buf
[strlen("M-SEARCH")])) {
745 ssdp_parse_msearch(sm
, &addr
, buf
);
749 /* Ignore anything else */
754 * ssdp_listener_start - Set up for receiving discovery (UDP) packets
755 * @sm: WPS UPnP state machine from upnp_wps_device_init()
756 * Returns: 0 on success, -1 on failure
758 * The SSDP listerner is stopped by calling ssdp_listener_stop().
760 int ssdp_listener_start(struct upnp_wps_device_sm
*sm
)
763 struct sockaddr_in addr
;
764 struct ip_mreq mcast_addr
;
766 /* per UPnP spec, keep IP packet time to live (TTL) small */
767 unsigned char ttl
= 4;
769 sm
->ssdp_sd
= sd
= socket(AF_INET
, SOCK_DGRAM
, 0);
772 if (fcntl(sd
, F_SETFL
, O_NONBLOCK
) != 0)
774 if (setsockopt(sd
, SOL_SOCKET
, SO_REUSEADDR
, &on
, sizeof(on
)))
776 os_memset(&addr
, 0, sizeof(addr
));
777 addr
.sin_family
= AF_INET
;
778 addr
.sin_addr
.s_addr
= htonl(INADDR_ANY
);
779 addr
.sin_port
= htons(UPNP_MULTICAST_PORT
);
780 if (bind(sd
, (struct sockaddr
*) &addr
, sizeof(addr
)))
782 os_memset(&mcast_addr
, 0, sizeof(mcast_addr
));
783 mcast_addr
.imr_interface
.s_addr
= htonl(INADDR_ANY
);
784 mcast_addr
.imr_multiaddr
.s_addr
= inet_addr(UPNP_MULTICAST_ADDRESS
);
785 if (setsockopt(sd
, IPPROTO_IP
, IP_ADD_MEMBERSHIP
,
786 (char *) &mcast_addr
, sizeof(mcast_addr
)))
788 if (setsockopt(sd
, IPPROTO_IP
, IP_MULTICAST_TTL
,
791 if (eloop_register_sock(sd
, EVENT_TYPE_READ
, ssdp_listener_handler
,
794 sm
->ssdp_sd_registered
= 1;
799 wpa_printf(MSG_ERROR
, "WPS UPnP: ssdp_listener_start failed");
800 ssdp_listener_stop(sm
);
806 * add_ssdp_network - Add routing entry for SSDP
807 * @net_if: Selected network interface name
808 * Returns: 0 on success, -1 on failure
810 * This function assures that the multicast address will be properly
811 * handled by Linux networking code (by a modification to routing tables).
812 * This must be done per network interface. It really only needs to be done
813 * once after booting up, but it does not hurt to call this more frequently
816 int add_ssdp_network(char *net_if
)
822 struct sockaddr_in
*sin
;
827 os_memset(&rt
, 0, sizeof(rt
));
828 sock
= socket(AF_INET
, SOCK_DGRAM
, 0);
833 sin
= aliasing_hide_typecast(&rt
.rt_dst
, struct sockaddr_in
);
834 sin
->sin_family
= AF_INET
;
836 sin
->sin_addr
.s_addr
= inet_addr(SSDP_TARGET
);
837 sin
= aliasing_hide_typecast(&rt
.rt_genmask
, struct sockaddr_in
);
838 sin
->sin_family
= AF_INET
;
840 sin
->sin_addr
.s_addr
= inet_addr(SSDP_NETMASK
);
841 rt
.rt_flags
= RTF_UP
;
842 if (ioctl(sock
, SIOCADDRT
, &rt
) < 0) {
843 if (errno
== EPERM
) {
844 wpa_printf(MSG_DEBUG
, "add_ssdp_network: No "
845 "permissions to add routing table entry");
846 /* Continue to allow testing as non-root */
847 } else if (errno
!= EEXIST
) {
848 wpa_printf(MSG_INFO
, "add_ssdp_network() ioctl errno "
849 "%d (%s)", errno
, strerror(errno
));
861 #else /* __linux__ */
863 #endif /* __linux__ */
868 * ssdp_open_multicast - Open socket for sending multicast SSDP messages
869 * @sm: WPS UPnP state machine from upnp_wps_device_init()
870 * Returns: 0 on success, -1 on failure
872 int ssdp_open_multicast(struct upnp_wps_device_sm
*sm
)
875 /* per UPnP-arch-DeviceArchitecture, 1. Discovery, keep IP packet
876 * time to live (TTL) small */
877 unsigned char ttl
= 4;
879 sm
->multicast_sd
= sd
= socket(AF_INET
, SOCK_DGRAM
, 0);
883 #if 0 /* maybe ok if we sometimes block on writes */
884 if (fcntl(sd
, F_SETFL
, O_NONBLOCK
) != 0)
888 if (setsockopt(sd
, IPPROTO_IP
, IP_MULTICAST_IF
,
889 &sm
->ip_addr
, sizeof(sm
->ip_addr
)))
891 if (setsockopt(sd
, IPPROTO_IP
, IP_MULTICAST_TTL
,
895 #if 0 /* not needed, because we don't receive using multicast_sd */
898 mreq
.imr_multiaddr
.s_addr
= inet_addr(UPNP_MULTICAST_ADDRESS
);
899 mreq
.imr_interface
.s_addr
= sm
->ip_addr
;
900 wpa_printf(MSG_DEBUG
, "WPS UPnP: Multicast addr 0x%x if addr "
902 mreq
.imr_multiaddr
.s_addr
,
903 mreq
.imr_interface
.s_addr
);
904 if (setsockopt(sd
, IPPROTO_IP
, IP_ADD_MEMBERSHIP
, &mreq
,
906 wpa_printf(MSG_ERROR
,
907 "WPS UPnP: setsockopt "
908 "IP_ADD_MEMBERSHIP errno %d (%s)",
909 errno
, strerror(errno
));
913 #endif /* not needed */
916 * TODO: What about IP_MULTICAST_LOOP? It seems to be on by default?
917 * which aids debugging I suppose but isn't really necessary?