mhopf@innominate.com add tcp/udp ports
diff --git a/ebtables.8 b/ebtables.8
index 7a88518..d19cd04 100644
--- a/ebtables.8
+++ b/ebtables.8
@@ -372,6 +372,19 @@
 The flag
 .B --ip-proto
 is an alias for this option.
+.TP
+.BR "--ip-source-port " "[!] \fIport\fP[:\fIport\fP]"
+The source port or port range for the ip protocols 6 (TCP) and 17
+(UDP). If the first port is omitted, "0" is assumed; if the last
+is omitted, "65535" is assumed. The flag
+.B --ip-sport
+is an alias for this option.
+.TP
+.BR "--ip-destination-port " "[!] \fIport\fP[:\fIport\fP]"
+The destination port or port range for ip protocols 6 (TCP) and
+17 (UDP). The flag
+.B --ip-dport
+is an alias for this option.
 .SS arp
 Specify arp specific fields. These will only work if the protocol equals
 .BR ARP " or " RARP .
diff --git a/extensions/ebt_ip.c b/extensions/ebt_ip.c
index 7080505..1351b42 100644
--- a/extensions/ebt_ip.c
+++ b/extensions/ebt_ip.c
@@ -1,7 +1,36 @@
+/*
+ *  ebtables ebt_ip: IP extension module for userspace
+ * 
+ *  Authors:
+ *   Bart De Schuymer <bart.de.schuymer@pandora.be>
+ *
+ *  Changes:
+ *    added ip-sport and ip-dport; parsing of port arguments is
+ *    based on code from iptables-1.2.7a
+ *    Innominate Security Technologies AG <mhopf@innominate.com>
+ *    September, 2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <getopt.h>
+#include <netdb.h>
 #include "../include/ebtables_u.h"
 #include <linux/netfilter_bridge/ebt_ip.h>
 
@@ -9,16 +38,22 @@
 #define IP_DEST   '2'
 #define IP_myTOS  '3' // include/bits/in.h seems to already define IP_TOS
 #define IP_PROTO  '4'
+#define IP_SPORT  '5'
+#define IP_DPORT  '6'
 
 static struct option opts[] =
 {
-	{ "ip-source"     , required_argument, 0, IP_SOURCE },
-	{ "ip-src"        , required_argument, 0, IP_SOURCE },
-	{ "ip-destination", required_argument, 0, IP_DEST   },
-	{ "ip-dst"        , required_argument, 0, IP_DEST   },
-	{ "ip-tos"        , required_argument, 0, IP_myTOS  },
-	{ "ip-protocol"   , required_argument, 0, IP_PROTO  },
-	{ "ip-proto"      , required_argument, 0, IP_PROTO  },
+	{ "ip-source"           , required_argument, 0, IP_SOURCE },
+	{ "ip-src"              , required_argument, 0, IP_SOURCE },
+	{ "ip-destination"      , required_argument, 0, IP_DEST   },
+	{ "ip-dst"              , required_argument, 0, IP_DEST   },
+	{ "ip-tos"              , required_argument, 0, IP_myTOS  },
+	{ "ip-protocol"         , required_argument, 0, IP_PROTO  },
+	{ "ip-proto"            , required_argument, 0, IP_PROTO  },
+	{ "ip-source-port"      , required_argument, 0, IP_SPORT  },
+	{ "ip-sport"            , required_argument, 0, IP_SPORT  },
+	{ "ip-destination-port" , required_argument, 0, IP_DPORT  },
+	{ "ip-dport"            , required_argument, 0, IP_DPORT  },
 	{ 0 }
 };
 
@@ -127,6 +162,56 @@
 	return buf;
 }
 
+// transform a protocol and service name into a port number
+static uint16_t parse_port(const char *protocol, const char *name)
+{
+	struct servent *service;
+	char *end;
+	int port;
+
+	port = strtol(name, &end, 10);
+	if (*end != '\0') {
+		if (protocol && 
+		    (service = getservbyname(name, protocol)) != NULL)
+			return ntohs(service->s_port);
+	}
+	else if (port >= 0 || port <= 0xFFFF) {
+		return port;
+	}
+	print_error("Problem with specified %s port '%s'", 
+		    protocol?protocol:"", name);
+	return 0; /* never reached */
+}
+
+static void
+parse_port_range(const char *protocol, const char *portstring, uint16_t *ports)
+{
+	char *buffer;
+	char *cp;
+	
+	buffer = strdup(portstring);
+	if ((cp = strchr(buffer, ':')) == NULL)
+		ports[0] = ports[1] = parse_port(protocol, buffer);
+	else {
+		*cp = '\0';
+		cp++;
+		ports[0] = buffer[0] ? parse_port(protocol, buffer) : 0;
+		ports[1] = cp[0] ? parse_port(protocol, cp) : 0xFFFF;
+		
+		if (ports[0] > ports[1])
+			print_error("Invalid portrange (min > max)");
+	}
+	free(buffer);
+}
+
+static void print_port_range(uint16_t *ports)
+{
+	if (ports[0] == ports[1])
+		printf("%d ", ntohs(ports[0]));
+	else
+		printf("%d:%d ", ntohs(ports[0]), ntohs(ports[1]));
+}
+
 static void print_help()
 {
 	printf(
@@ -134,7 +219,9 @@
 "--ip-src    [!] address[/mask]: ip source specification\n"
 "--ip-dst    [!] address[/mask]: ip destination specification\n"
 "--ip-tos    [!] tos           : ip tos specification\n"
-"--ip-proto  [!] protocol      : ip protocol specification\n");
+"--ip-proto  [!] protocol      : ip protocol specification\n"
+"--ip-sport  [!] port[:port]   : tcp/udp source port or port range\n"
+"--ip-dport  [!] port[:port]   : tcp/udp destination port or port range\n");
 }
 
 static void init(struct ebt_entry_match *match)
@@ -149,6 +236,8 @@
 #define OPT_DEST   0x02
 #define OPT_TOS    0x04
 #define OPT_PROTO  0x08
+#define OPT_SPORT  0x10
+#define OPT_DPORT  0x20
 static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry,
    unsigned int *flags, struct ebt_entry_match **match)
 {
@@ -183,6 +272,27 @@
 			   &ipinfo->dmsk);
 		break;
 
+	case IP_SPORT:
+	case IP_DPORT:
+		if (c == IP_SPORT) {
+			check_option(flags, OPT_SPORT);
+			ipinfo->bitmask |= EBT_IP_SPORT;
+			if (check_inverse(optarg))
+				ipinfo->invflags |= EBT_IP_SPORT;
+		} else {
+			check_option(flags, OPT_DPORT);
+			ipinfo->bitmask |= EBT_IP_DPORT;
+			if (check_inverse(optarg))
+				ipinfo->invflags |= EBT_IP_DPORT;
+		}
+		if (optind > argc)
+			print_error("Missing port argument");
+		if (c == IP_SPORT)
+			parse_port_range(NULL, argv[optind - 1], ipinfo->sport);
+		else
+			parse_port_range(NULL, argv[optind - 1], ipinfo->dport);
+		break;
+
 	case IP_myTOS:
 		check_option(flags, OPT_TOS);
 		if (check_inverse(optarg))
@@ -219,9 +329,19 @@
    const struct ebt_entry_match *match, const char *name,
    unsigned int hookmask, unsigned int time)
 {
+ 	struct ebt_ip_info *ipinfo = (struct ebt_ip_info *)match->data;
+
 	if (entry->ethproto != ETH_P_IP || entry->invflags & EBT_IPROTO)
 		print_error("For IP filtering the protocol must be "
 		            "specified as IPv4");
+
+ 	if (ipinfo->bitmask & (EBT_IP_SPORT|EBT_IP_DPORT) &&
+ 		(!ipinfo->bitmask & EBT_IP_PROTO || 
+ 		ipinfo->invflags & EBT_IP_PROTO ||
+ 		(ipinfo->protocol!=IPPROTO_TCP && 
+ 			ipinfo->protocol!=IPPROTO_UDP)))
+ 		print_error("For port filtering the IP protocol must be "
+ 		            "either 6 (tcp) or 17 (udp)");
 }
 
 static void print(const struct ebt_u_entry *entry,
@@ -260,6 +380,20 @@
 			printf("! ");
 		printf("%d ", ipinfo->protocol);
 	}
+	if (ipinfo->bitmask & EBT_IP_SPORT) {
+		printf("--ip-sport ");
+		if (ipinfo->invflags & EBT_IP_SPORT) {
+			printf("! ");
+		}
+		print_port_range(ipinfo->sport);
+	}
+	if (ipinfo->bitmask & EBT_IP_DPORT) {
+		printf("--ip-dport ");
+		if (ipinfo->invflags & EBT_IP_DPORT) {
+			printf("! ");
+		}
+		print_port_range(ipinfo->dport);
+	}
 }
 
 static int compare(const struct ebt_entry_match *m1,
@@ -292,6 +426,14 @@
 		if (ipinfo1->protocol != ipinfo2->protocol)
 			return 0;
 	}
+	if (ipinfo1->bitmask & EBT_IP_SPORT) {
+		if (ipinfo1->sport != ipinfo2->sport)
+			return 0;
+	}
+	if (ipinfo1->bitmask & EBT_IP_DPORT) {
+		if (ipinfo1->dport != ipinfo2->dport)
+			return 0;
+	}
 	return 1;
 }