diff --git a/README.md b/README.md index a0aa2a8..71ae237 100644 --- a/README.md +++ b/README.md @@ -190,6 +190,13 @@ below for details. * `--metadata-timeout TIMEOUT` Set the timeout for HTTP-based metadata exchange. Default is 2.0 seconds. + * `--reply-port PORT` + Set the source port for outgoing multicast messages, so that replies will + use this as the destination port. + This is useful for firewalls that do not detect incoming unicast replies + to a multicast as part of the flow, so the port needs to be fixed in order + to be allowed manually. + * `-s`, `--shortlog` Use a shorter logging format that only includes the level and message. diff --git a/man/wsdd.8 b/man/wsdd.8 index b6131f1..eddac30 100644 --- a/man/wsdd.8 +++ b/man/wsdd.8 @@ -54,6 +54,13 @@ For IPv6, only link local addresses are actually considered as noted above. \fB\-\-metadata-timeout\ \fITIMEOUT\fR Set the timeout for HTTP-based metadata exchange. Default is 2.0 seconds. .TP +\fB\-\-reply-port\ \fPORT\fR +Set the source port for outgoing multicast messages, so that replies will +use this as the destination port. +This is useful for firewalls that do not detect incoming unicast replies +to a multicast as part of the flow, so the port needs to be fixed in order +to be allowed manually. +.TP \fB\-s\fR, \fB\-\-shortlog\fR Use a shorter logging format that only includes the level and message. This is useful in cases where the logging mechanism, like systemd on Linux, diff --git a/src/wsdd.py b/src/wsdd.py index 085e868..fda30c6 100755 --- a/src/wsdd.py +++ b/src/wsdd.py @@ -263,6 +263,13 @@ def init_v6(self) -> None: self.mc_send_socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_HOPS, args.hoplimit) self.mc_send_socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_IF, idx) + # bind multicast socket to interface address and a user-provided port (or random if unspecified) + # this allows not-so-smart firewalls to whitelist another port to allow incoming replies + try: + self.mc_send_socket.bind((str(self.address), args.reply_port, 0, idx)) + except OSError: + logger.error('specified port {} already in use for {}'.format(args.reply_port, str(self.address))) + self.listen_address = (self.address.address_str, WSD_HTTP_PORT, 0, idx) def init_v4(self) -> None: @@ -292,6 +299,13 @@ def init_v4(self) -> None: self.mc_send_socket.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_LOOP, struct.pack('B', 0)) self.mc_send_socket.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, struct.pack('B', args.hoplimit)) + # bind multicast socket to interface address and a user-provided port (or random if unspecified) + # this allows not-so-smart firewalls to whitelist another port to allow incoming replies + try: + self.mc_send_socket.bind((self.address.address_str, args.reply_port)) + except OSError: + logger.error('specified port {} already in use for {}'.format(args.reply_port, self.address.address_str)) + self.listen_address = (self.address.address_str, WSD_HTTP_PORT) def add_handler(self, socket: socket.socket, handler: INetworkPacketHandler) -> None: @@ -1862,6 +1876,11 @@ def parse_args() -> None: '--metadata-timeout', help='set timeout for HTTP-based metadata exchange', default=2.0) + parser.add_argument( + '--reply-port', + help='recieve replies to multicast on this port', + type=int, + default=0) args = parser.parse_args(sys.argv[1:])