udhcp: optional support for non-standard DHCP ports (+300 bytes when selected)

diff --git a/include/usage.h b/include/usage.h
index 989ed91..fb88e9b 100644
--- a/include/usage.h
+++ b/include/usage.h
@@ -3892,7 +3892,7 @@
 
 #define udhcpc_trivial_usage \
        "[-Cfbnqtv] [-c CID] [-V VCLS] [-H HOSTNAME] [-i INTERFACE]\n" \
-       "	[-p pidfile] [-r IP] [-s script] [-O dhcp-option]..."
+       "	[-p pidfile] [-r IP] [-s script] [-O dhcp-option]..." USE_FEATURE_UDHCP_PORT(" [-P N]")
 #define udhcpc_full_usage \
 	USE_GETOPT_LONG( \
        "	-V,--vendorclass=CLASSID	Vendor class identifier" \
@@ -3913,6 +3913,9 @@
        "\n	-q,--quit	Quit after obtaining lease" \
        "\n	-R,--release	Release IP on quit" \
        "\n	-O,--request-option=OPT	Request DHCP option OPT from server" \
+	USE_FEATURE_UDHCP_PORT( \
+       "\n	-P,--client-port N  Use port N instead of default 68" \
+	) \
 	USE_FEATURE_UDHCPC_ARPING( \
        "\n	-a,--arping	Use arping to validate offered address" \
 	) \
@@ -3936,18 +3939,24 @@
        "\n	-q		Quit after obtaining lease" \
        "\n	-R		Release IP on quit" \
        "\n	-O OPT		Request DHCP option OPT from server" \
+	USE_FEATURE_UDHCP_PORT( \
+       "\n	-P N		Use port N instead of default 68" \
+	) \
 	USE_FEATURE_UDHCPC_ARPING( \
        "\n	-a		Use arping to validate offered address" \
 	) \
 	)
 
 #define udhcpd_trivial_usage \
-       "[-fS] [configfile]" \
+       "[-fS]" USE_FEATURE_UDHCP_PORT(" [-P N]") " [configfile]" \
 
 #define udhcpd_full_usage \
        "DHCP server" \
        "\n	-f	Run in foreground" \
-       "\n	-S	Log to syslog too"
+       "\n	-S	Log to syslog too" \
+	USE_FEATURE_UDHCP_PORT( \
+       "\n	-P N	Use port N instead of default 67" \
+	)
 
 #define umount_trivial_usage \
        "[flags] FILESYSTEM|DIRECTORY"
diff --git a/networking/udhcp/Config.in b/networking/udhcp/Config.in
index 26cc8f5..ff0e4e2 100644
--- a/networking/udhcp/Config.in
+++ b/networking/udhcp/Config.in
@@ -63,6 +63,13 @@
 	  is really available. The client will DHCPDECLINE the offer if the
 	  address is in use, and restart the discover process.
 
+config FEATURE_UDHCP_PORT
+	bool "Enable '-P port' option for udhcpd and udhcpc"
+	default n
+	depends on APP_UDHCPD || APP_UDHCPC
+	help
+	  At the cost of ~300 bytes, enables -P port option.
+	  This feature is typically not needed.
 
 config FEATURE_UDHCP_DEBUG
 	bool "Compile udhcp with noisy debugging messages"
diff --git a/networking/udhcp/clientsocket.c b/networking/udhcp/clientsocket.c
index 954db53..b5534f8 100644
--- a/networking/udhcp/clientsocket.c
+++ b/networking/udhcp/clientsocket.c
@@ -33,8 +33,8 @@
 #include <linux/filter.h>
 
 #include "common.h"
-
-#define SERVER_AND_CLIENT_PORTS  ((SERVER_PORT << 16) + CLIENT_PORT)
+#include "dhcpd.h"
+#include "dhcpc.h"
 
 int raw_socket(int ifindex)
 {
@@ -62,6 +62,7 @@
 	 *
 	 * TODO: make conditional?
 	 */
+#define SERVER_AND_CLIENT_PORTS  ((67 << 16) + 68)
 	static const struct sock_filter filter_instr[] = {
 		/* check for udp */
 		BPF_STMT(BPF_LD|BPF_B|BPF_ABS, 9),
@@ -89,10 +90,13 @@
 	fd = xsocket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP));
 	DEBUG("got raw socket fd %d", fd);
 
-	/* Ignoring error (kernel may lack support for this) */
-	if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter_prog,
+	if (SERVER_PORT == 67 && CLIENT_PORT == 68) {
+		/* Use only if standard ports are in use */
+		/* Ignoring error (kernel may lack support for this) */
+		if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter_prog,
 				sizeof(filter_prog)) >= 0)
-		DEBUG("attached filter to raw socket fd %d", fd);
+			DEBUG("attached filter to raw socket fd %d", fd);
+	}
 
 	sock.sll_family = AF_PACKET;
 	sock.sll_protocol = htons(ETH_P_IP);
diff --git a/networking/udhcp/common.h b/networking/udhcp/common.h
index b1d629b..9b26678 100644
--- a/networking/udhcp/common.h
+++ b/networking/udhcp/common.h
@@ -14,9 +14,6 @@
 
 #define DEFAULT_SCRIPT  "/usr/share/udhcpc/default.script"
 
-#define SERVER_PORT  67
-#define CLIENT_PORT  68
-
 extern const uint8_t MAC_BCAST_ADDR[6]; /* six all-ones */
 
 /*** packet.h ***/
diff --git a/networking/udhcp/dhcpc.c b/networking/udhcp/dhcpc.c
index f54bc08..7fca184 100644
--- a/networking/udhcp/dhcpc.c
+++ b/networking/udhcp/dhcpc.c
@@ -133,6 +133,7 @@
 {
 	uint8_t *temp, *message;
 	char *str_c, *str_V, *str_h, *str_F, *str_r, *str_T, *str_A, *str_t;
+	USE_FEATURE_UDHCP_PORT(char *str_P;)
 	llist_t *list_O = NULL;
 #if ENABLE_FEATURE_UDHCPC_ARPING
 	char *str_W;
@@ -181,6 +182,7 @@
 		OPT_a = 1 << 20,
 		OPT_W = 1 << 21,
 #endif
+		OPT_P = 1 << 22,
 	};
 #if ENABLE_GETOPT_LONG
 	static const char udhcpc_longopts[] ALIGN1 =
@@ -207,9 +209,16 @@
 		"arping\0"         No_argument       "a"
 #endif
 		"request-option\0" Required_argument "O"
+#if ENABLE_FEATURE_UDHCP_PORT
+		"client-port\0"	   Required_argument "P"
+#endif
 		;
 #endif
 	/* Default options. */
+#if ENABLE_FEATURE_UDHCP_PORT
+	SERVER_PORT = 67;
+	CLIENT_PORT = 68;
+#endif
 	client_config.interface = "eth0";
 	client_config.script = DEFAULT_SCRIPT;
 
@@ -220,11 +229,13 @@
 #endif
 	opt = getopt32(argv, "c:CV:fbH:h:F:i:np:qRr:s:T:t:vSA:"
 		USE_FEATURE_UDHCPC_ARPING("aW:")
+		USE_FEATURE_UDHCP_PORT("P:")
 		"O:"
 		, &str_c, &str_V, &str_h, &str_h, &str_F
 		, &client_config.interface, &client_config.pidfile, &str_r
 		, &client_config.script, &str_T, &str_t, &str_A
 		USE_FEATURE_UDHCPC_ARPING(, &str_W)
+		USE_FEATURE_UDHCP_PORT(, &str_P)
 		, &list_O
 		);
 
@@ -276,6 +287,12 @@
 		openlog(applet_name, LOG_PID, LOG_LOCAL0);
 		logmode |= LOGMODE_SYSLOG;
 	}
+#if ENABLE_FEATURE_UDHCP_PORT
+	if (opt & OPT_P) {
+		CLIENT_PORT = xatou16(str_P);
+		SERVER_PORT = CLIENT_PORT - 1;
+	}
+#endif
 	while (list_O) {
 		int n = index_in_strings(dhcp_option_strings, list_O->data);
 		if (n < 0)
diff --git a/networking/udhcp/dhcpc.h b/networking/udhcp/dhcpc.h
index bc05754..4b8f3ec 100644
--- a/networking/udhcp/dhcpc.h
+++ b/networking/udhcp/dhcpc.h
@@ -29,11 +29,19 @@
 	uint8_t *hostname;              /* Optional hostname to use */
 	uint8_t *fqdn;                  /* Optional fully qualified domain name to use */
 	int ifindex;                    /* Index number of the interface to use */
+	uint16_t port;
 	uint8_t arp[6];                 /* Our arp address */
 	uint8_t opt_mask[256 / 8];      /* Bitmask of options to send (-O option) */
 };
 
-#define client_config (*(struct client_config_t*)&bb_common_bufsiz1)
+/* server_config sits in 1st half of bb_common_bufsiz1 */
+#define client_config (*(struct client_config_t*)(&bb_common_bufsiz1[COMMON_BUFSIZE/2]))
+
+#if ENABLE_FEATURE_UDHCP_PORT
+#define CLIENT_PORT (client_config.port)
+#else
+#define CLIENT_PORT 68
+#endif
 
 
 /*** clientpacket.h ***/
diff --git a/networking/udhcp/dhcpd.c b/networking/udhcp/dhcpd.c
index 45f445b..eb7323d 100644
--- a/networking/udhcp/dhcpd.c
+++ b/networking/udhcp/dhcpd.c
@@ -12,6 +12,7 @@
 
 #include <syslog.h>
 #include "common.h"
+#include "dhcpc.h"
 #include "dhcpd.h"
 #include "options.h"
 
@@ -35,8 +36,14 @@
 	unsigned opt;
 	struct option_set *option;
 	struct dhcpOfferedAddr *lease, static_lease;
+	USE_FEATURE_UDHCP_PORT(char *str_P;)
 
-	opt = getopt32(argv, "fS");
+#if ENABLE_FEATURE_UDHCP_PORT
+	SERVER_PORT = 67;
+	CLIENT_PORT = 68;
+#endif
+
+	opt = getopt32(argv, "fS" USE_FEATURE_UDHCP_PORT("P:", &str_P));
 	argv += optind;
 
 	if (!(opt & 1)) { /* no -f */
@@ -48,7 +55,12 @@
 		openlog(applet_name, LOG_PID, LOG_LOCAL0);
 		logmode |= LOGMODE_SYSLOG;
 	}
-
+#if ENABLE_FEATURE_UDHCP_PORT
+	if (opt & 4) { /* -P */
+		SERVER_PORT = xatou16(str_P);
+		CLIENT_PORT = SERVER_PORT + 1;
+	}
+#endif
 	/* Would rather not do read_config before daemonization -
 	 * otherwise NOMMU machines will parse config twice */
 	read_config(argv[0] ? argv[0] : DHCPD_CONF_FILE);
diff --git a/networking/udhcp/dhcpd.h b/networking/udhcp/dhcpd.h
index 216b7ab..fe45ca3 100644
--- a/networking/udhcp/dhcpd.h
+++ b/networking/udhcp/dhcpd.h
@@ -1,5 +1,6 @@
 /* vi: set sw=4 ts=4: */
 /* dhcpd.h */
+
 #ifndef _DHCPD_H
 #define _DHCPD_H
 
@@ -27,6 +28,7 @@
 
 struct server_config_t {
 	uint32_t server;                /* Our IP, in network order */
+	uint16_t port;
 	/* start,end are in host order: we need to compare start <= ip <= end */
 	uint32_t start_ip;              /* Start address of leases, in host order */
 	uint32_t end_ip;                /* End of leases, in host order */
@@ -55,6 +57,13 @@
 };
 
 #define server_config (*(struct server_config_t*)&bb_common_bufsiz1)
+/* client_config sits in 2nd half of bb_common_bufsiz1 */
+
+#if ENABLE_FEATURE_UDHCP_PORT
+#define SERVER_PORT (server_config.port)
+#else
+#define SERVER_PORT 67
+#endif
 
 extern struct dhcpOfferedAddr *leases;
 
diff --git a/networking/udhcp/dhcprelay.c b/networking/udhcp/dhcprelay.c
index c243cc1..def1bc2 100644
--- a/networking/udhcp/dhcprelay.c
+++ b/networking/udhcp/dhcprelay.c
@@ -14,6 +14,7 @@
 #include "options.h"
 
 /* constants */
+#define SERVER_PORT      67
 #define SELECT_TIMEOUT    5 /* select timeout in sec. */
 #define MAX_LIFETIME   2*60 /* lifetime of an xid entry in sec. */
 
diff --git a/networking/udhcp/serverpacket.c b/networking/udhcp/serverpacket.c
index 764b9a8..1dc7233 100644
--- a/networking/udhcp/serverpacket.c
+++ b/networking/udhcp/serverpacket.c
@@ -21,6 +21,7 @@
  */
 
 #include "common.h"
+#include "dhcpc.h"
 #include "dhcpd.h"
 #include "options.h"