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 MAX_MSEARCH 20 /* max simultaneous M-SEARCH replies ongoing */
28 #define SSDP_TARGET "239.0.0.0"
29 #define SSDP_NETMASK "255.0.0.0"
32 /* Check tokens for equality, where tokens consist of letters, digits,
33 * underscore and hyphen, and are matched case insensitive.
35 static int token_eq(const char *s1
, const char *s2
)
44 if (isalpha(c1
) && isupper(c1
))
46 if (isalpha(c2
) && isupper(c2
))
48 end1
= !(isalnum(c1
) || c1
== '_' || c1
== '-');
49 end2
= !(isalnum(c2
) || c2
== '_' || c2
== '-');
50 if (end1
|| end2
|| c1
!= c2
)
53 return end1
&& end2
; /* reached end of both words? */
57 /* Return length of token (see above for definition of token) */
58 static int token_length(const char *s
)
60 const char *begin
= s
;
63 int end
= !(isalnum(c
) || c
== '_' || c
== '-');
71 /* return length of interword separation.
72 * This accepts only spaces/tabs and thus will not traverse a line
75 static int word_separation_length(const char *s
)
77 const char *begin
= s
;
80 if (c
== ' ' || c
== '\t')
88 /* No. of chars through (including) end of line */
89 static int line_length(const char *l
)
92 while (*lp
&& *lp
!= '\n')
100 /* No. of chars excluding trailing whitespace */
101 static int line_length_stripped(const char *l
)
103 const char *lp
= l
+ line_length(l
);
104 while (lp
> l
&& !isgraph(lp
[-1]))
110 static int str_starts(const char *str
, const char *start
)
112 return os_strncmp(str
, start
, os_strlen(start
)) == 0;
116 /***************************************************************************
118 * These are multicast to the world to tell them we are here.
119 * The individual packets are spread out in time to limit loss,
120 * and then after a much longer period of time the whole sequence
121 * is repeated again (for NOTIFYs only).
122 **************************************************************************/
125 * next_advertisement - Build next message and advance the state machine
126 * @a: Advertisement state
127 * @islast: Buffer for indicating whether this is the last message (= 1)
128 * Returns: The new message (caller is responsible for freeing this)
130 * Note: next_advertisement is shared code with msearchreply_* functions
132 static struct wpabuf
*
133 next_advertisement(struct upnp_wps_device_sm
*sm
,
134 struct advertisement_state_machine
*a
, int *islast
)
138 char uuid_string
[80];
141 uuid_bin2str(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 sm
->ip_addr_text
, 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
;
249 os_memset(&dest
, 0, sizeof(dest
));
250 dest
.sin_family
= AF_INET
;
251 dest
.sin_addr
.s_addr
= inet_addr(UPNP_MULTICAST_ADDRESS
);
252 dest
.sin_port
= htons(UPNP_MULTICAST_PORT
);
255 msg
= next_advertisement(sm
, a
, &islast
);
258 if (sendto(sm
->multicast_sd
, wpabuf_head(msg
), wpabuf_len(msg
),
259 0, (struct sockaddr
*) &dest
, sizeof(dest
)) < 0) {
260 wpa_printf(MSG_INFO
, "WPS UPnP: Advertisement sendto "
261 "failed: %d (%s)", errno
, strerror(errno
));
269 static void advertisement_state_machine_handler(void *eloop_data
,
272 struct upnp_wps_device_sm
*sm
= user_ctx
;
273 struct advertisement_state_machine
*a
= &sm
->advertisement
;
275 int next_timeout_msec
= 100;
276 int next_timeout_sec
= 0;
277 struct sockaddr_in dest
;
281 * Each is sent twice (in case lost) w/ 100 msec delay between;
282 * spec says no more than 3 times.
283 * One pair for rootdevice, one pair for uuid, and a pair each for
284 * each of the two urns.
285 * The entire sequence must be repeated before cache control timeout
286 * (which is min 1800 seconds),
287 * recommend random portion of half of the advertised cache control age
288 * to ensure against loss... perhaps 1800/4 + rand*1800/4 ?
289 * Delay random interval < 100 msec prior to initial sending.
293 wpa_printf(MSG_MSGDUMP
, "WPS UPnP: Advertisement state=%d", a
->state
);
294 msg
= next_advertisement(sm
, a
, &islast
);
298 os_memset(&dest
, 0, sizeof(dest
));
299 dest
.sin_family
= AF_INET
;
300 dest
.sin_addr
.s_addr
= inet_addr(UPNP_MULTICAST_ADDRESS
);
301 dest
.sin_port
= htons(UPNP_MULTICAST_PORT
);
303 if (sendto(sm
->multicast_sd
, wpabuf_head(msg
), wpabuf_len(msg
), 0,
304 (struct sockaddr
*) &dest
, sizeof(dest
)) == -1) {
305 wpa_printf(MSG_ERROR
, "WPS UPnP: Advertisement sendto failed:"
306 "%d (%s)", errno
, strerror(errno
));
307 next_timeout_msec
= 0;
308 next_timeout_sec
= 10; /* ... later */
310 a
->state
= 0; /* wrap around */
311 if (a
->type
== ADVERTISE_DOWN
) {
312 wpa_printf(MSG_DEBUG
, "WPS UPnP: ADVERTISE_DOWN->UP");
313 a
->type
= ADVERTISE_UP
;
314 /* do it all over again right away */
318 * Start over again after a long timeout
321 next_timeout_msec
= 0;
322 os_get_random((void *) &r
, sizeof(r
));
323 next_timeout_sec
= UPNP_CACHE_SEC
/ 4 +
324 (((UPNP_CACHE_SEC
/ 4) * r
) >> 16);
325 sm
->advertise_count
++;
326 wpa_printf(MSG_DEBUG
, "WPS UPnP: ADVERTISE_UP (#%u); "
328 sm
->advertise_count
, next_timeout_sec
);
336 eloop_register_timeout(next_timeout_sec
, next_timeout_msec
,
337 advertisement_state_machine_handler
, NULL
, sm
);
342 * advertisement_state_machine_start - Start SSDP advertisements
343 * @sm: WPS UPnP state machine from upnp_wps_device_init()
344 * Returns: 0 on success, -1 on failure
346 int advertisement_state_machine_start(struct upnp_wps_device_sm
*sm
)
348 struct advertisement_state_machine
*a
= &sm
->advertisement
;
349 int next_timeout_msec
;
351 advertisement_state_machine_stop(sm
, 0);
354 * Start out advertising down, this automatically switches
355 * to advertising up which signals our restart.
357 a
->type
= ADVERTISE_DOWN
;
359 /* (other fields not used here) */
361 /* First timeout should be random interval < 100 msec */
362 next_timeout_msec
= (100 * (os_random() & 0xFF)) >> 8;
363 return eloop_register_timeout(0, next_timeout_msec
,
364 advertisement_state_machine_handler
,
369 /***************************************************************************
371 * These are very similar to the multicast advertisements, with some
372 * small changes in data content; and they are sent (UDP) to a specific
373 * unicast address instead of multicast.
374 * They are sent in response to a UDP M-SEARCH packet.
375 **************************************************************************/
378 * msearchreply_state_machine_stop - Stop M-SEARCH reply state machine
379 * @a: Selected advertisement/reply state
381 void msearchreply_state_machine_stop(struct advertisement_state_machine
*a
)
383 wpa_printf(MSG_DEBUG
, "WPS UPnP: M-SEARCH stop");
384 dl_list_del(&a
->list
);
389 static void msearchreply_state_machine_handler(void *eloop_data
,
392 struct advertisement_state_machine
*a
= user_ctx
;
393 struct upnp_wps_device_sm
*sm
= eloop_data
;
395 int next_timeout_msec
= 100;
396 int next_timeout_sec
= 0;
400 * Each response is sent twice (in case lost) w/ 100 msec delay
401 * between; spec says no more than 3 times.
402 * One pair for rootdevice, one pair for uuid, and a pair each for
403 * each of the two urns.
406 /* TODO: should only send the requested response types */
408 wpa_printf(MSG_MSGDUMP
, "WPS UPnP: M-SEARCH reply state=%d (%s:%d)",
409 a
->state
, inet_ntoa(a
->client
.sin_addr
),
410 ntohs(a
->client
.sin_port
));
411 msg
= next_advertisement(sm
, a
, &islast
);
416 * Send it on the multicast socket to avoid having to set up another
419 if (sendto(sm
->multicast_sd
, wpabuf_head(msg
), wpabuf_len(msg
), 0,
420 (struct sockaddr
*) &a
->client
, sizeof(a
->client
)) < 0) {
421 wpa_printf(MSG_DEBUG
, "WPS UPnP: M-SEARCH reply sendto "
422 "errno %d (%s) for %s:%d",
423 errno
, strerror(errno
),
424 inet_ntoa(a
->client
.sin_addr
),
425 ntohs(a
->client
.sin_port
));
426 /* Ignore error and hope for the best */
430 wpa_printf(MSG_DEBUG
, "WPS UPnP: M-SEARCH reply done");
431 msearchreply_state_machine_stop(a
);
436 wpa_printf(MSG_MSGDUMP
, "WPS UPnP: M-SEARCH reply in %d.%03d sec",
437 next_timeout_sec
, next_timeout_msec
);
438 eloop_register_timeout(next_timeout_sec
, next_timeout_msec
,
439 msearchreply_state_machine_handler
, sm
, a
);
444 * msearchreply_state_machine_start - Reply to M-SEARCH discovery request
445 * @sm: WPS UPnP state machine from upnp_wps_device_init()
446 * @client: Client address
447 * @mx: Maximum delay in seconds
449 * Use TTL of 4 (this was done when socket set up).
450 * A response should be given in randomized portion of min(MX,120) seconds
452 * UPnP-arch-DeviceArchitecture, 1.2.3:
453 * To be found, a device must send a UDP response to the source IP address and
454 * port that sent the request to the multicast channel. Devices respond if the
455 * ST header of the M-SEARCH request is "ssdp:all", "upnp:rootdevice", "uuid:"
456 * followed by a UUID that exactly matches one advertised by the device.
458 static void msearchreply_state_machine_start(struct upnp_wps_device_sm
*sm
,
459 struct sockaddr_in
*client
,
462 struct advertisement_state_machine
*a
;
463 int next_timeout_sec
;
464 int next_timeout_msec
;
467 replies
= dl_list_len(&sm
->msearch_replies
);
468 wpa_printf(MSG_DEBUG
, "WPS UPnP: M-SEARCH reply start (%d "
469 "outstanding)", replies
);
470 if (replies
>= MAX_MSEARCH
) {
471 wpa_printf(MSG_INFO
, "WPS UPnP: Too many outstanding "
476 a
= os_zalloc(sizeof(*a
));
479 a
->type
= MSEARCH_REPLY
;
481 os_memcpy(&a
->client
, client
, sizeof(*client
));
482 /* Wait time depending on MX value */
483 next_timeout_msec
= (1000 * mx
* (os_random() & 0xFF)) >> 8;
484 next_timeout_sec
= next_timeout_msec
/ 1000;
485 next_timeout_msec
= next_timeout_msec
% 1000;
486 if (eloop_register_timeout(next_timeout_sec
, next_timeout_msec
,
487 msearchreply_state_machine_handler
, sm
,
489 /* No way to recover (from malloc failure) */
492 /* Remember for future cleanup */
493 dl_list_add(&sm
->msearch_replies
, &a
->list
);
497 wpa_printf(MSG_INFO
, "WPS UPnP: M-SEARCH reply failure!");
498 eloop_cancel_timeout(msearchreply_state_machine_handler
, sm
, a
);
504 * ssdp_parse_msearch - Process a received M-SEARCH
505 * @sm: WPS UPnP state machine from upnp_wps_device_init()
506 * @client: Client address
507 * @data: NULL terminated M-SEARCH message
509 * Given that we have received a header w/ M-SEARCH, act upon it
511 * Format of M-SEARCH (case insensitive!):
513 * First line must be:
514 * M-SEARCH * HTTP/1.1
515 * Other lines in arbitrary order:
516 * HOST:239.255.255.250:1900
517 * ST:<varies -- must match>
518 * MAN:"ssdp:discover"
521 * It should be noted that when Microsoft Vista is still learning its IP
522 * address, it sends out host lines like: HOST:[FF02::C]:1900
524 static void ssdp_parse_msearch(struct upnp_wps_device_sm
*sm
,
525 struct sockaddr_in
*client
, const char *data
)
527 #ifndef CONFIG_NO_STDOUT_DEBUG
528 const char *start
= data
;
529 #endif /* CONFIG_NO_STDOUT_DEBUG */
532 int got_st
= 0, st_match
= 0;
538 * Skip first line M-SEARCH * HTTP/1.1
539 * (perhaps we should check remainder of the line for syntax)
541 data
+= line_length(data
);
543 /* Parse remaining lines */
544 for (; *data
!= '\0'; data
+= line_length(data
)) {
545 end
= data
+ line_length_stripped(data
);
546 if (token_eq(data
, "host")) {
547 /* The host line indicates who the packet
548 * is addressed to... but do we really care?
549 * Note that Microsoft sometimes does funny
550 * stuff with the HOST: line.
553 data
+= token_length(data
);
554 data
+= word_separation_length(data
);
558 data
+= word_separation_length(data
);
559 /* UPNP_MULTICAST_ADDRESS */
560 if (!str_starts(data
, "239.255.255.250"))
562 data
+= os_strlen("239.255.255.250");
564 if (!str_starts(data
, ":1900"))
567 #endif /* could be */
570 } else if (token_eq(data
, "st")) {
571 /* There are a number of forms; we look
572 * for one that matches our case.
575 data
+= token_length(data
);
576 data
+= word_separation_length(data
);
580 data
+= word_separation_length(data
);
581 if (str_starts(data
, "ssdp:all")) {
585 if (str_starts(data
, "upnp:rootdevice")) {
589 if (str_starts(data
, "uuid:")) {
590 char uuid_string
[80];
591 data
+= os_strlen("uuid:");
592 uuid_bin2str(sm
->wps
->uuid
, uuid_string
,
593 sizeof(uuid_string
));
594 if (str_starts(data
, uuid_string
))
599 /* FIX: should we really reply to IGD string? */
600 if (str_starts(data
, "urn:schemas-upnp-org:device:"
601 "InternetGatewayDevice:1")) {
606 if (str_starts(data
, "urn:schemas-wifialliance-org:"
607 "service:WFAWLANConfig:1")) {
611 if (str_starts(data
, "urn:schemas-wifialliance-org:"
612 "device:WFADevice:1")) {
617 } else if (token_eq(data
, "man")) {
618 data
+= token_length(data
);
619 data
+= word_separation_length(data
);
623 data
+= word_separation_length(data
);
624 if (!str_starts(data
, "\"ssdp:discover\"")) {
625 wpa_printf(MSG_DEBUG
, "WPS UPnP: Unexpected "
626 "M-SEARCH man-field");
631 } else if (token_eq(data
, "mx")) {
632 data
+= token_length(data
);
633 data
+= word_separation_length(data
);
637 data
+= word_separation_length(data
);
642 /* ignore anything else */
644 if (!got_host
|| !got_st
|| !got_man
|| !got_mx
|| mx
< 0) {
645 wpa_printf(MSG_DEBUG
, "WPS UPnP: Invalid M-SEARCH: %d %d %d "
646 "%d mx=%d", got_host
, got_st
, got_man
, got_mx
, mx
);
650 wpa_printf(MSG_DEBUG
, "WPS UPnP: Ignored M-SEARCH (no ST "
655 mx
= 120; /* UPnP-arch-DeviceArchitecture, 1.2.3 */
656 msearchreply_state_machine_start(sm
, client
, mx
);
660 wpa_printf(MSG_INFO
, "WPS UPnP: Failed to parse M-SEARCH");
661 wpa_printf(MSG_MSGDUMP
, "WPS UPnP: M-SEARCH data:\n%s", start
);
665 /* Listening for (UDP) discovery (M-SEARCH) packets */
668 * ssdp_listener_stop - Stop SSDP listered
669 * @sm: WPS UPnP state machine from upnp_wps_device_init()
671 * This function stops the SSDP listener that was started by calling
672 * ssdp_listener_start().
674 void ssdp_listener_stop(struct upnp_wps_device_sm
*sm
)
676 if (sm
->ssdp_sd_registered
) {
677 eloop_unregister_sock(sm
->ssdp_sd
, EVENT_TYPE_READ
);
678 sm
->ssdp_sd_registered
= 0;
681 if (sm
->ssdp_sd
!= -1) {
686 eloop_cancel_timeout(msearchreply_state_machine_handler
, sm
,
691 static void ssdp_listener_handler(int sd
, void *eloop_ctx
, void *sock_ctx
)
693 struct upnp_wps_device_sm
*sm
= sock_ctx
;
694 struct sockaddr_in addr
; /* client address */
697 char buf
[MULTICAST_MAX_READ
], *pos
;
699 addr_len
= sizeof(addr
);
700 nread
= recvfrom(sm
->ssdp_sd
, buf
, sizeof(buf
) - 1, 0,
701 (struct sockaddr
*) &addr
, &addr_len
);
704 buf
[nread
] = '\0'; /* need null termination for algorithm */
706 if (str_starts(buf
, "NOTIFY ")) {
708 * Silently ignore NOTIFYs to avoid filling debug log with
714 pos
= os_strchr(buf
, '\n');
717 wpa_printf(MSG_MSGDUMP
, "WPS UPnP: Received SSDP packet from %s:%d: "
718 "%s", inet_ntoa(addr
.sin_addr
), ntohs(addr
.sin_port
), buf
);
722 /* Parse first line */
723 if (os_strncasecmp(buf
, "M-SEARCH", os_strlen("M-SEARCH")) == 0 &&
724 !isgraph(buf
[strlen("M-SEARCH")])) {
725 ssdp_parse_msearch(sm
, &addr
, buf
);
729 /* Ignore anything else */
733 int ssdp_listener_open(void)
735 struct sockaddr_in addr
;
736 struct ip_mreq mcast_addr
;
738 /* per UPnP spec, keep IP packet time to live (TTL) small */
739 unsigned char ttl
= 4;
742 sd
= socket(AF_INET
, SOCK_DGRAM
, 0);
745 if (fcntl(sd
, F_SETFL
, O_NONBLOCK
) != 0)
747 if (setsockopt(sd
, SOL_SOCKET
, SO_REUSEADDR
, &on
, sizeof(on
)))
749 os_memset(&addr
, 0, sizeof(addr
));
750 addr
.sin_family
= AF_INET
;
751 addr
.sin_addr
.s_addr
= htonl(INADDR_ANY
);
752 addr
.sin_port
= htons(UPNP_MULTICAST_PORT
);
753 if (bind(sd
, (struct sockaddr
*) &addr
, sizeof(addr
)))
755 os_memset(&mcast_addr
, 0, sizeof(mcast_addr
));
756 mcast_addr
.imr_interface
.s_addr
= htonl(INADDR_ANY
);
757 mcast_addr
.imr_multiaddr
.s_addr
= inet_addr(UPNP_MULTICAST_ADDRESS
);
758 if (setsockopt(sd
, IPPROTO_IP
, IP_ADD_MEMBERSHIP
,
759 (char *) &mcast_addr
, sizeof(mcast_addr
)))
761 if (setsockopt(sd
, IPPROTO_IP
, IP_MULTICAST_TTL
,
775 * ssdp_listener_start - Set up for receiving discovery (UDP) packets
776 * @sm: WPS UPnP state machine from upnp_wps_device_init()
777 * Returns: 0 on success, -1 on failure
779 * The SSDP listener is stopped by calling ssdp_listener_stop().
781 int ssdp_listener_start(struct upnp_wps_device_sm
*sm
)
783 sm
->ssdp_sd
= ssdp_listener_open();
785 if (eloop_register_sock(sm
->ssdp_sd
, EVENT_TYPE_READ
,
786 ssdp_listener_handler
, NULL
, sm
))
788 sm
->ssdp_sd_registered
= 1;
793 wpa_printf(MSG_ERROR
, "WPS UPnP: ssdp_listener_start failed");
794 ssdp_listener_stop(sm
);
800 * add_ssdp_network - Add routing entry for SSDP
801 * @net_if: Selected network interface name
802 * Returns: 0 on success, -1 on failure
804 * This function assures that the multicast address will be properly
805 * handled by Linux networking code (by a modification to routing tables).
806 * This must be done per network interface. It really only needs to be done
807 * once after booting up, but it does not hurt to call this more frequently
810 int add_ssdp_network(const char *net_if
)
816 struct sockaddr_in
*sin
;
821 os_memset(&rt
, 0, sizeof(rt
));
822 sock
= socket(AF_INET
, SOCK_DGRAM
, 0);
826 rt
.rt_dev
= (char *) net_if
;
827 sin
= aliasing_hide_typecast(&rt
.rt_dst
, struct sockaddr_in
);
828 sin
->sin_family
= AF_INET
;
830 sin
->sin_addr
.s_addr
= inet_addr(SSDP_TARGET
);
831 sin
= aliasing_hide_typecast(&rt
.rt_genmask
, struct sockaddr_in
);
832 sin
->sin_family
= AF_INET
;
834 sin
->sin_addr
.s_addr
= inet_addr(SSDP_NETMASK
);
835 rt
.rt_flags
= RTF_UP
;
836 if (ioctl(sock
, SIOCADDRT
, &rt
) < 0) {
837 if (errno
== EPERM
) {
838 wpa_printf(MSG_DEBUG
, "add_ssdp_network: No "
839 "permissions to add routing table entry");
840 /* Continue to allow testing as non-root */
841 } else if (errno
!= EEXIST
) {
842 wpa_printf(MSG_INFO
, "add_ssdp_network() ioctl errno "
843 "%d (%s)", errno
, strerror(errno
));
855 #else /* __linux__ */
857 #endif /* __linux__ */
861 int ssdp_open_multicast_sock(u32 ip_addr
)
864 /* per UPnP-arch-DeviceArchitecture, 1. Discovery, keep IP packet
865 * time to live (TTL) small */
866 unsigned char ttl
= 4;
868 sd
= socket(AF_INET
, SOCK_DGRAM
, 0);
872 #if 0 /* maybe ok if we sometimes block on writes */
873 if (fcntl(sd
, F_SETFL
, O_NONBLOCK
) != 0)
877 if (setsockopt(sd
, IPPROTO_IP
, IP_MULTICAST_IF
,
878 &ip_addr
, sizeof(ip_addr
)))
880 if (setsockopt(sd
, IPPROTO_IP
, IP_MULTICAST_TTL
,
884 #if 0 /* not needed, because we don't receive using multicast_sd */
887 mreq
.imr_multiaddr
.s_addr
= inet_addr(UPNP_MULTICAST_ADDRESS
);
888 mreq
.imr_interface
.s_addr
= ip_addr
;
889 wpa_printf(MSG_DEBUG
, "WPS UPnP: Multicast addr 0x%x if addr "
891 mreq
.imr_multiaddr
.s_addr
,
892 mreq
.imr_interface
.s_addr
);
893 if (setsockopt(sd
, IPPROTO_IP
, IP_ADD_MEMBERSHIP
, &mreq
,
895 wpa_printf(MSG_ERROR
,
896 "WPS UPnP: setsockopt "
897 "IP_ADD_MEMBERSHIP errno %d (%s)",
898 errno
, strerror(errno
));
902 #endif /* not needed */
905 * TODO: What about IP_MULTICAST_LOOP? It seems to be on by default?
906 * which aids debugging I suppose but isn't really necessary?
914 * ssdp_open_multicast - Open socket for sending multicast SSDP messages
915 * @sm: WPS UPnP state machine from upnp_wps_device_init()
916 * Returns: 0 on success, -1 on failure
918 int ssdp_open_multicast(struct upnp_wps_device_sm
*sm
)
920 sm
->multicast_sd
= ssdp_open_multicast_sock(sm
->ip_addr
);
921 if (sm
->multicast_sd
< 0)