From: Arno DUBOIS Date: Thu, 26 Sep 2024 23:28:08 +0000 (+0200) Subject: iptv: allow to limit UDP ports for unicast inputs X-Git-Url: http://git.ipfire.org/gitweb/gitweb.cgi?a=commitdiff_plain;h=b69dac9299fde5fd43200a9a56c43bba1ce145cf;p=thirdparty%2Ftvheadend.git iptv: allow to limit UDP ports for unicast inputs --- diff --git a/src/config.c b/src/config.c index c5bd4ef4d..a8b853d45 100644 --- a/src/config.c +++ b/src/config.c @@ -2199,9 +2199,13 @@ const idclass_t config_class = { .number = 6, }, { - .name = N_("Miscellaneous Settings"), + .name = N_("Ports settings"), .number = 7, }, + { + .name = N_("Miscellaneous Settings"), + .number = 8, + }, {} }, .ic_properties = (const property_t[]){ @@ -2647,6 +2651,28 @@ const idclass_t config_class = { , .group = 6 }, + { + .type = PT_INT, + .id = "rtsp_udp_min_port", + .name = N_("RTSP UDP minimum port"), + .desc = N_("When using RTSP IPTV, this correspond to the " + "minimum port bind on the client (this server), " + "sent to the server. This is especially useful " + "when using firewalls and NAT or containers."), + .off = offsetof(config_t, rtsp_udp_min_port), + .opts = PO_EXPERT, + .group = 7, + }, + { + .type = PT_INT, + .id = "rtsp_udp_max_port", + .name = N_("RTSP UDP maximum port"), + .desc = N_("Same as above, but for the maximum allowed " + "port. Note that each stream requires two ports."), + .off = offsetof(config_t, rtsp_udp_max_port), + .opts = PO_EXPERT, + .group = 7, + }, { .type = PT_STR, .id = "http_user_agent", @@ -2654,7 +2680,7 @@ const idclass_t config_class = { .desc = N_("The user agent string for the build-in HTTP client."), .off = offsetof(config_t, http_user_agent), .opts = PO_HIDDEN | PO_EXPERT, - .group = 7, + .group = 8, }, { .type = PT_INT, @@ -2663,7 +2689,7 @@ const idclass_t config_class = { .desc = N_("Set the number of threads for IPTV to split load " "across more CPUs."), .off = offsetof(config_t, iptv_tpool_count), - .group = 7, + .group = 8, }, { .type = PT_INT, @@ -2680,7 +2706,7 @@ const idclass_t config_class = { .off = offsetof(config_t, dscp), .list = config_class_dscp_list, .opts = PO_EXPERT | PO_DOC_NLIST, - .group = 7, + .group = 8, }, { .type = PT_U32, @@ -2690,7 +2716,7 @@ const idclass_t config_class = { "there is a delay receiving CA keys. "), .off = offsetof(config_t, descrambler_buffer), .opts = PO_EXPERT, - .group = 7, + .group = 8, }, { .type = PT_BOOL, @@ -2701,7 +2727,7 @@ const idclass_t config_class = { "It may cause issues with some clients / players."), .off = offsetof(config_t, parser_backlog), .opts = PO_EXPERT, - .group = 7, + .group = 8, }, { .type = PT_STR, @@ -2714,7 +2740,7 @@ const idclass_t config_class = { .off = offsetof(config_t, muxconf_path), .notify = config_muxconfpath_notify, .opts = PO_ADVANCED, - .group = 7, + .group = 8, }, { .type = PT_BOOL, @@ -2722,7 +2748,7 @@ const idclass_t config_class = { .name = N_("Parse HbbTV info"), .desc = N_("Parse HbbTV information from services."), .off = offsetof(config_t, hbbtv), - .group = 7, + .group = 8, .def.i = 1, }, { @@ -2734,7 +2760,7 @@ const idclass_t config_class = { "the system clock (normally only root)."), .off = offsetof(config_t, tvhtime_update_enabled), .opts = PO_EXPERT, - .group = 7, + .group = 8, }, { .type = PT_BOOL, @@ -2746,7 +2772,7 @@ const idclass_t config_class = { "performance is not that great."), .off = offsetof(config_t, tvhtime_ntp_enabled), .opts = PO_EXPERT, - .group = 7, + .group = 8, }, { .type = PT_U32, @@ -2758,7 +2784,7 @@ const idclass_t config_class = { "excessive oscillations on the system clock."), .off = offsetof(config_t, tvhtime_tolerance), .opts = PO_EXPERT, - .group = 7, + .group = 8, }, #if ENABLE_VAAPI { diff --git a/src/config.h b/src/config.h index fe1f9b931..5ae12e8c9 100644 --- a/src/config.h +++ b/src/config.h @@ -75,6 +75,8 @@ typedef struct config { char *hdhomerun_ip; char *local_ip; int local_port; + int rtsp_udp_min_port; + int rtsp_udp_max_port; uint32_t hdhomerun_server_tuner_count; char *hdhomerun_server_model_name; int hdhomerun_server_enable; diff --git a/src/input/mpegts/iptv/iptv_rtcp.c b/src/input/mpegts/iptv/iptv_rtcp.c index 5370dc0b9..2441cdace 100644 --- a/src/input/mpegts/iptv/iptv_rtcp.c +++ b/src/input/mpegts/iptv/iptv_rtcp.c @@ -385,7 +385,7 @@ rtcp_connect(rtcp_t * info, char *url, char *host, int port, char *interface, ch if (info->connection == NULL) { rtcp_conn = udp_bind(LS_IPTV, nicename, NULL, 0, NULL, interface, - IPTV_BUF_SIZE, 1024); + IPTV_BUF_SIZE, 1024, 0); if (rtcp_conn == NULL || rtcp_conn == UDP_FATAL_ERROR) { tvhwarn(LS_IPTV, "%s - Unable to bind, RTCP won't be available", nicename); diff --git a/src/input/mpegts/iptv/iptv_rtsp.c b/src/input/mpegts/iptv/iptv_rtsp.c index 66d83c2c7..ec3c3db65 100644 --- a/src/input/mpegts/iptv/iptv_rtsp.c +++ b/src/input/mpegts/iptv/iptv_rtsp.c @@ -18,6 +18,7 @@ */ #include "tvheadend.h" +#include "config.h" #include "iptv_private.h" #include "iptv_rtcp.h" #include "http.h" @@ -263,6 +264,8 @@ iptv_rtsp_start http_client_t *hc; udp_connection_t *rtp, *rtcp; int r; + int rtp_port; + int max_rtp_port; if (!(hc = http_client_connect(im, RTSP_VERSION_1_0, u->scheme, u->host, u->port, NULL))) @@ -272,15 +275,38 @@ iptv_rtsp_start hc->hc_rtsp_user = strdup(u->user); if (u->pass) hc->hc_rtsp_pass = strdup(u->pass); + + rtp_port = config.rtsp_udp_min_port; + max_rtp_port = config.rtsp_udp_max_port; - if (udp_bind_double(&rtp, &rtcp, - LS_IPTV, "rtp", "rtcp", - NULL, 0, NULL, - 128*1024, 16384, 4*1024, 4*1024) < 0) { + if (!max_rtp_port) { + rtp_port = 0; + } + + if (rtp_port > max_rtp_port) + { + tvherror(LS_IPTV, "UDP minimum port is set higher than the UDP maximum port"); http_client_close(hc); return SM_CODE_TUNING_FAILED; } + while (udp_bind_double(&rtp, &rtcp, + LS_IPTV, "rtp", "rtcp", + NULL, rtp_port, NULL, + 128 * 1024, 16384, 4 * 1024, 4 * 1024, 1) < 0) + { + if (!rtp_port) { + tvherror(LS_IPTV, "could not bind a random UDP port for RTP"); + http_client_close(hc); + return SM_CODE_TUNING_FAILED; + } else if (max_rtp_port && rtp_port >= max_rtp_port - 2) { + tvherror(LS_IPTV, "all the UDP ports allocated are used, please reduce the number of simultaneous IPTV streams or increase the number of ports in the global settings"); + http_client_close(hc); + return SM_CODE_TUNING_FAILED; + } + rtp_port += 2; + } + hc->hc_hdr_received = iptv_rtsp_header; hc->hc_data_received = iptv_rtsp_data; hc->hc_handle_location = 1; /* allow redirects */ diff --git a/src/input/mpegts/iptv/iptv_udp.c b/src/input/mpegts/iptv/iptv_udp.c index b2ab863ff..c3bff2621 100644 --- a/src/input/mpegts/iptv/iptv_udp.c +++ b/src/input/mpegts/iptv/iptv_udp.c @@ -42,7 +42,7 @@ iptv_udp_start /* Note: url->user is used for specifying multicast source address (SSM) here. The URL format is rtp://@: */ conn = udp_bind(LS_IPTV, im->mm_nicename, url->host, url->port, url->user, - im->mm_iptv_interface, IPTV_BUF_SIZE, 4*1024); + im->mm_iptv_interface, IPTV_BUF_SIZE, 4*1024, 0); if (conn == UDP_FATAL_ERROR) return SM_CODE_TUNING_FAILED; if (conn == NULL) diff --git a/src/input/mpegts/satip/satip_frontend.c b/src/input/mpegts/satip/satip_frontend.c index 1c414da68..2d7e7ad28 100644 --- a/src/input/mpegts/satip/satip_frontend.c +++ b/src/input/mpegts/satip/satip_frontend.c @@ -1729,7 +1729,7 @@ new_tune: if (udp_bind_double(&rtp, &rtcp, LS_SATIP, "rtp", "rtcp", satip_frontend_bindaddr(lfe), lfe->sf_udp_rtp_port, - NULL, SATIP_BUF_SIZE, 16384, 4*1024, 4*1024) < 0) { + NULL, SATIP_BUF_SIZE, 16384, 4*1024, 4*1024, 0) < 0) { satip_frontend_tuning_error(lfe, tr); goto done; } diff --git a/src/satip/rtsp.c b/src/satip/rtsp.c index 98f85724e..24efae9d7 100644 --- a/src/satip/rtsp.c +++ b/src/satip/rtsp.c @@ -1562,7 +1562,7 @@ rtsp_process_play(http_connection_t *hc, int cmd) LS_SATIPS, "rtsp", "rtcp", (rtp_src_ip != NULL && rtp_src_ip[0] != '\0') ? rtp_src_ip : rtsp_ip, 0, NULL, 4*1024, 4*1024, - RTP_BUFSIZE, RTCP_BUFSIZE)) { + RTP_BUFSIZE, RTCP_BUFSIZE, 0)) { errcode = HTTP_STATUS_INTERNAL; goto error; } diff --git a/src/udp.c b/src/udp.c index b0c3c1e5c..40cb8308b 100644 --- a/src/udp.c +++ b/src/udp.c @@ -154,7 +154,7 @@ udp_get_solip( void ) udp_connection_t * udp_bind ( int subsystem, const char *name, const char *bindaddr, int port, const char *multicast_src, - const char *ifname, int rxsize, int txsize ) + const char *ifname, int rxsize, int txsize, int bind_fail_allowed ) { int fd, ifindex, reuse = 1; udp_connection_t *uc; @@ -186,8 +186,8 @@ udp_bind ( int subsystem, const char *name, uc->fd = fd; - /* Mark reuse address */ - if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse))) { + /* Mark reuse address, only wanted and required for Multicast in UDP (and TCP, but not here, then) */ + if (uc->multicast && setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse))) { tvherror(subsystem, "%s - failed to reuse address for socket [%s]", name, strerror(errno)); udp_close(uc); @@ -207,9 +207,11 @@ udp_bind ( int subsystem, const char *name, /* Bind useful for receiver subsystem (not for udp streamer) */ if (subsystem != LS_UDP) { if (bind(fd, (struct sockaddr *)&uc->ip, sizeof(struct sockaddr_in))) { - inet_ntop(AF_INET, &IP_AS_V4(&uc->ip, addr), buf, sizeof(buf)); - tvherror(subsystem, "%s - cannot bind %s:%hu [e=%s]", - name, buf, ntohs(IP_AS_V4(&uc->ip, port)), strerror(errno)); + if (!bind_fail_allowed) { + inet_ntop(AF_INET, &IP_AS_V4(&uc->ip, addr), buf, sizeof(buf)); + tvherror(subsystem, "%s - cannot bind %s:%hu [e=%s]", + name, buf, ntohs(IP_AS_V4(&uc->ip, port)), strerror(errno)); + } goto error; } } @@ -280,9 +282,11 @@ udp_bind ( int subsystem, const char *name, /* Bind */ if (bind(fd, (struct sockaddr *)&uc->ip, sizeof(struct sockaddr_in6))) { - inet_ntop(AF_INET6, &IP_AS_V6(&uc->ip, addr), buf, sizeof(buf)); - tvherror(subsystem, "%s - cannot bind %s:%hu [e=%s]", - name, buf, ntohs(IP_AS_V6(&uc->ip, port)), strerror(errno)); + if (!bind_fail_allowed) { + inet_ntop(AF_INET6, &IP_AS_V6(&uc->ip, addr), buf, sizeof(buf)); + tvherror(subsystem, "%s - cannot bind %s:%hu [e=%s]", + name, buf, ntohs(IP_AS_V6(&uc->ip, port)), strerror(errno)); + } goto error; } @@ -334,7 +338,7 @@ udp_bind_double ( udp_connection_t **_u1, udp_connection_t **_u2, int subsystem, const char *name1, const char *name2, const char *host, int port, const char *ifname, int rxsize1, int rxsize2, - int txsize1, int txsize2 ) + int txsize1, int txsize2, int bind_fail_allowed ) { udp_connection_t *u1 = NULL, *u2 = NULL; udp_connection_t *ucs[10]; @@ -342,13 +346,13 @@ udp_bind_double ( udp_connection_t **_u1, udp_connection_t **_u2, memset(&ucs, 0, sizeof(ucs)); while (1) { - u1 = udp_bind(subsystem, name1, host, port, NULL, ifname, rxsize1, txsize1); + u1 = udp_bind(subsystem, name1, host, port, NULL, ifname, rxsize1, txsize1, bind_fail_allowed); if (u1 == NULL || u1 == UDP_FATAL_ERROR) goto fail; port2 = ntohs(IP_PORT(u1->ip)); /* RTP port should be even, RTCP port should be odd */ if ((port2 % 2) == 0) { - u2 = udp_bind(subsystem, name2, host, port2 + 1, NULL, ifname, rxsize2, txsize2); + u2 = udp_bind(subsystem, name2, host, port2 + 1, NULL, ifname, rxsize2, txsize2, bind_fail_allowed); if (u2 != NULL && u2 != UDP_FATAL_ERROR) break; } diff --git a/src/udp.h b/src/udp.h index 55b6dfad3..75328961a 100644 --- a/src/udp.h +++ b/src/udp.h @@ -45,13 +45,13 @@ typedef struct udp_connection { udp_connection_t * udp_bind ( int subsystem, const char *name, const char *bindaddr, int port, const char *multicast_src, - const char *ifname, int rxsize, int txsize ); + const char *ifname, int rxsize, int txsize, int bind_fail_allowed ); int udp_bind_double ( udp_connection_t **_u1, udp_connection_t **_u2, int subsystem, const char *name1, const char *name2, const char *host, int port, const char *ifname, int rxsize1, int rxsize2, - int txsize1, int txsize2 ); + int txsize1, int txsize2, int bind_fail_allowed ); udp_connection_t * udp_sendinit ( int subsystem, const char *name, const char *ifname, int txsize ); diff --git a/src/upnp.c b/src/upnp.c index f6a3c649f..51dac4291 100644 --- a/src/upnp.c +++ b/src/upnp.c @@ -130,11 +130,11 @@ upnp_thread( void *aux ) multicast = udp_bind(LS_UPNP, "upnp_thread_multicast", "239.255.255.250", 1900, NULL, - NULL, 32*1024, 32*1024); + NULL, 32*1024, 32*1024, 0); if (multicast == NULL || multicast == UDP_FATAL_ERROR) goto error; unicast = udp_bind(LS_UPNP, "upnp_thread_unicast", bindaddr, 0, NULL, - NULL, 32*1024, 32*1024); + NULL, 32*1024, 32*1024, 0); if (unicast == NULL || unicast == UDP_FATAL_ERROR) goto error; diff --git a/src/webui/webui.c b/src/webui/webui.c index 51e643515..d2669389d 100644 --- a/src/webui/webui.c +++ b/src/webui/webui.c @@ -1245,7 +1245,7 @@ udp_stream_service(http_connection_t *hc, service_t *service, int weight) if (!(uc = udp_bind(LS_UDP, "udp_streamer", address, port, NULL, - NULL, 1024, 188*7))) { + NULL, 1024, 188*7, 0))) { tvhwarn(LS_WEBUI, "Could not create and bind udp socket"); return res; } @@ -1498,7 +1498,7 @@ udp_stream_channel(http_connection_t *hc, channel_t *ch, int weight) if (!(uc = udp_bind(LS_UDP, "udp_streamer", address, port, NULL, - NULL, 1024, 188*7))) { + NULL, 1024, 188*7, 0))) { tvhwarn(LS_WEBUI, "Could not create and bind udp socket"); return res; }