Skip to contentSkip to conversation

convert mport to multiport

PATCH In review Pablo Neira 3 participants 8 messages Sep 26, 2004 – Sep 27, 2004

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 convert mport to multiport

Hi Patrick,

Attached a patch that puts mport and multiport together. It applies to latest bk snapshot.

I've also added some clean ups like the use of a count var inside the ipt_multiport instead of using 65535 and the pflag set to mark the end of the array of ports. I've tested and it works fine for me.

Also attached the patch for iptables and man page. If missing something, please let me know.

regards,
Pablo

===== net/ipv4/netfilter/ipt_multiport.c 1.8 vs edited =====

net/ipv4/netfilter/ipt_multiport.c +33 -20
@@ -29,18 +29,38 @@
 
 /* Returns 1 if the port is matched by the test, 0 otherwise. */
 static inline int
-ports_match(const u_int16_t *portlist, enum ipt_multiport_flags flags,
-	    u_int8_t count, u_int16_t src, u_int16_t dst)
+ports_match(const struct ipt_multiport *minfo, u_int16_t src, u_int16_t dst)
 {
-	unsigned int i;
-	for (i=0; i<count; i++) {
-		if (flags != IPT_MULTIPORT_DESTINATION
-		    && portlist[i] == src)
-			return 1;
-
-		if (flags != IPT_MULTIPORT_SOURCE
-		    && portlist[i] == dst)
-			return 1;
+	unsigned int i, m;
+	u_int16_t pflags = minfo->pflags;
+	u_int8_t count = minfo->count;
+	u_int16_t s, e;
+
+	for (i=0, m=1; i < count; i++, m<<=1) {
+		s = minfo->ports[i];
+
+		if (pflags & m) {
+			/* range port matching */
+			e = minfo->ports[++i];
+			m <<= 1;
+			duprintf("src or dst matches with %d-%d?\n", s, e);
+
+			if (minfo->flags & IPT_MULTIPORT_SOURCE
+			    && src >= s && src <= e)
+				return 1;
+			if (minfo->flags & IPT_MULTIPORT_DESTINATION
+			    && dst >= s && dst <= e)
+				return 1;
+		} else {
+			/* exact port matching */
+			duprintf("src or dst matches with %d?\n", s);
+			if (minfo->flags & IPT_MULTIPORT_SOURCE
+			    && src == s)
+				return 1;
+			if (minfo->flags & IPT_MULTIPORT_DESTINATION
+			    && dst == s)
+				return 1;
+		}
 	}
 
 	return 0;
@@ -69,15 +89,11 @@
 		/* We've been asked to examine this packet, and we
 		 * can't.  Hence, no choice but to drop.
 		 */
-		duprintf("ipt_multiport:"
-			 " Dropping evil offset=0 tinygram.\n");
+		duprintf("ipt_multiport: Dropping evil offset=0 tinygram.\n");
 		*hotdrop = 1;
 		return 0;
 	}
-
-	return ports_match(multiinfo->ports,
-			   multiinfo->flags, multiinfo->count,
-			   ntohs(pptr[0]), ntohs(pptr[1]));
+	return ports_match(multiinfo, ntohs(pptr[0]), ntohs(pptr[1]));
 }
 
 /* Called when user tries to insert an entry of this type. */
@@ -89,9 +105,6 @@
 	   unsigned int hook_mask)
 {
 	const struct ipt_multiport *multiinfo = matchinfo;
-
-	if (matchsize != IPT_ALIGN(sizeof(struct ipt_multiport)))
-		return 0;
 
 	/* Must specify proto == TCP/UDP, no unknown flags or bad count */
 	return (ip->proto == IPPROTO_TCP || ip->proto == IPPROTO_UDP)

===== include/linux/netfilter_ipv4/ipt_multiport.h 1.1 vs edited =====

include/linux/netfilter_ipv4/ipt_multiport.h +18 -8
@@ -2,20 +2,30 @@
 #define _IPT_MULTIPORT_H
 #include <linux/netfilter_ipv4/ip_tables.h>
 
-enum ipt_multiport_flags
-{
-	IPT_MULTIPORT_SOURCE,
-	IPT_MULTIPORT_DESTINATION,
-	IPT_MULTIPORT_EITHER
+enum ipt_multiport_flags {
+	IPT_MULTIPORT_SOURCE_BIT = 0,
+	IPT_MULTIPORT_SOURCE = (1 << IPT_MULTIPORT_SOURCE_BIT),
+	
+	IPT_MULTIPORT_DESTINATION_BIT = 1,
+	IPT_MULTIPORT_DESTINATION = (1 << IPT_MULTIPORT_DESTINATION_BIT),
+
+	IPT_MULTIPORT_EITHER = IPT_MULTIPORT_SOURCE|IPT_MULTIPORT_DESTINATION,
 };
 
 #define IPT_MULTI_PORTS	15
 
-/* Must fit inside union ipt_matchinfo: 16 bytes */
+/* Must fit inside union ipt_matchinfo: 32 bytes */
+/* every entry in ports[] except for the last one has one bit in pflags
+ * associated with it. If this bit is set, the port is the first port of
+ * a portrange, with the next entry being the last.
+ * End of list is marked with pflags bit set and port=65535.
+ * If 14 ports are used (last one does not have a pflag), the last port
+ * is repeated to fill the last entry in ports[] */
 struct ipt_multiport
 {
-	u_int8_t flags;				/* Type of comparison */
-	u_int8_t count;				/* Number of ports */
+	u_int8_t flags:2;			/* Type of comparison */
+	u_int16_t pflags:14;			/* Port flags */
 	u_int16_t ports[IPT_MULTI_PORTS];	/* Ports */
+	u_int8_t count;                         /* Number of ports */
 };
 #endif /*_IPT_MULTIPORT_H*/

Index: extensions/libipt_multiport.c
===================================================================
RCS file: /cvspublic/iptables/extensions/libipt_multiport.c,v retrieving revision 1.8

libipt_multiport.c +47 -18

145 lines not on this page · raw message

Index: extensions/libipt_multiport.man
===================================================================
RCS file: /cvspublic/iptables/extensions/libipt_multiport.man,v retrieving revision 1.1

libipt_multiport.man +3 -3
diff -u -r1.1 libipt_multiport.man
@@ -4,16 +4,16 @@
 or
 .BR "-p udp" .
 .TP
-.BR "--source-ports " "\fIport\fP[,\fIport\fP[,\fIport\fP...]]"
+.BR "--source-ports " "\fIport\fP[,\fIport:port\fP[,\fIport\fP...]]"
 Match if the source port is one of the given ports.  The flag
 .B --sports
 is a convenient alias for this option.
 .TP
-.BR "--destination-ports " "\fIport\fP[,\fIport\fP[,\fIport\fP...]]"
+.BR "--destination-ports " "\fIport\fP[,\fIport:port\fP[,\fIport\fP...]]"
 Match if the destination port is one of the given ports.  The flag
 .B --dports
 is a convenient alias for this option.
 .TP
-.BR "--ports " "\fIport\fP[,\fIport\fP[,\fIport\fP...]]"
+.BR "--ports " "\fIport\fP[,\fIport:port\fP[,\fIport\fP...]]"
 Match if the both the source and destination ports are equal to each
 other and to one of the given ports.

Index: extensions/libipt_mport.c
===================================================================
RCS file: /cvspublic/iptables/extensions/libipt_mport.c,v retrieving revision 1.7

libipt_mport.c +0 -314

314 lines not on this page · raw message

Index: extensions/libipt_mport.man
===================================================================
RCS file: /cvspublic/iptables/extensions/libipt_mport.man,v retrieving revision 1.1

libipt_mport.man +0 -19
diff -u -r1.1 libipt_mport.man
@@ -1,19 +0,0 @@
-This module matches a set of source or destination ports.  Up to 15
-ports can be specified.  It can only be used in conjunction with
-.B "-p tcp"
-or
-.BR "-p udp" .
-.TP
-.BR "--source-ports " "\fIport\fP[,\fIport\fP[,\fIport\fP...]]"
-Match if the source port is one of the given ports.  The flag
-.B --sports
-is a convenient alias for this option.
-.TP
-.BR "--destination-ports " "\fIport\fP[,\fIport\fP[,\fIport\fP...]]"
-Match if the destination port is one of the given ports.  The flag
-.B --dports
-is a convenient alias for this option.
-.TP
-.BR "--ports " "\fIport\fP[,\fIport\fP[,\fIport\fP...]]"
-Match if the both the source and destination ports are equal to each
-other and to one of the given ports.
#1 lore raw

Hi again Patrick,

I think that some comments about the patch could help you to review it, so here we go :-)

Quoted 42 lines · Pablo Neira

===== net/ipv4/netfilter/ipt_multiport.c 1.8 vs edited =====

net/ipv4/netfilter/ipt_multiport.c +0 -0
@@ -29,18 +29,38 @@
 
 /* Returns 1 if the port is matched by the test, 0 otherwise. */

static inline int

net/ipv4/netfilter/ipt_multiport.c +1 -2
-ports_match(const u_int16_t *portlist, enum ipt_multiport_flags flags,
-	    u_int8_t count, u_int16_t src, u_int16_t dst)
+ports_match(const struct ipt_multiport *minfo, u_int16_t src, u_int16_t dst)

{

net/ipv4/netfilter/ipt_multiport.c +20 -9
-	unsigned int i;
-	for (i=0; i<count; i++) {
-		if (flags != IPT_MULTIPORT_DESTINATION
-		    && portlist[i] == src)
-			return 1;
-
-		if (flags != IPT_MULTIPORT_SOURCE
-		    && portlist[i] == dst)
-			return 1;
+	unsigned int i, m;
+	u_int16_t pflags = minfo->pflags;
+	u_int8_t count = minfo->count;
+	u_int16_t s, e;
+
+	for (i=0, m=1; i < count; i++, m<<=1) {
+		s = minfo->ports[i];
+
+		if (pflags & m) {
+			/* range port matching */
+			e = minfo->ports[++i];
+			m <<= 1;
+			duprintf("src or dst matches with %d-%d?\n", s, e);
+
+			if (minfo->flags & IPT_MULTIPORT_SOURCE
+			    && src >= s && src <= e)
+				return 1;
+			if (minfo->flags & IPT_MULTIPORT_DESTINATION
+			    && dst >= s && dst <= e)
+				return 1;

net/ipv4/netfilter/ipt_multiport.c line 63

Quoted 10 lines
net/ipv4/netfilter/ipt_multiport.c +10 -0
+		} else {
+			/* exact port matching */
+			duprintf("src or dst matches with %d?\n", s);
+			if (minfo->flags & IPT_MULTIPORT_SOURCE
+			    && src == s)
+				return 1;
+			if (minfo->flags & IPT_MULTIPORT_DESTINATION
+			    && dst == s)
+				return 1;
+		}
          ^^^

This is the part that I've cleaned up. mport way of doing this is also ok.

Quoted 22 lines
	}

	return 0;
net/ipv4/netfilter/ipt_multiport.c +2 -6
@@ -69,15 +89,11 @@
 		/* We've been asked to examine this packet, and we
 		 * can't.  Hence, no choice but to drop.
 		 */
-		duprintf("ipt_multiport:"
-			 " Dropping evil offset=0 tinygram.\n");
+		duprintf("ipt_multiport: Dropping evil offset=0 tinygram.\n");
 		*hotdrop = 1;
 		return 0;
 	}
-
-	return ports_match(multiinfo->ports,
-			   multiinfo->flags, multiinfo->count,
-			   ntohs(pptr[0]), ntohs(pptr[1]));
+	return ports_match(multiinfo, ntohs(pptr[0]), ntohs(pptr[1]));
 }
 
 /* Called when user tries to insert an entry of this type. */

net/ipv4/netfilter/ipt_multiport.c line 109

Quoted 8 lines
net/ipv4/netfilter/ipt_multiport.c +0 -3
@@ -89,9 +105,6 @@
 	   unsigned int hook_mask)
 {
 	const struct ipt_multiport *multiinfo = matchinfo;
-
-	if (matchsize != IPT_ALIGN(sizeof(struct ipt_multiport)))
-		return 0;
 
 
                 ^^^

Hm this check is done in the return, it's duplicated.

Quoted 15 lines
net/ipv4/netfilter/ipt_multiport.c +0 -0
 	/* Must specify proto == TCP/UDP, no unknown flags or bad count */
	return (ip->proto == IPPROTO_TCP || ip->proto == IPPROTO_UDP)

===== include/linux/netfilter_ipv4/ipt_multiport.h 1.1 vs edited =====

include/linux/netfilter_ipv4/ipt_multiport.h +0 -5
@@ -2,20 +2,30 @@
 #define _IPT_MULTIPORT_H
 #include <linux/netfilter_ipv4/ip_tables.h>
 
-enum ipt_multiport_flags
-{
-	IPT_MULTIPORT_SOURCE,
-	IPT_MULTIPORT_DESTINATION,
-	IPT_MULTIPORT_EITHER

include/linux/netfilter_ipv4/ipt_multiport.h line 14

Quoted 10 lines
include/linux/netfilter_ipv4/ipt_multiport.h +8 -0
+enum ipt_multiport_flags {
+	IPT_MULTIPORT_SOURCE_BIT = 0,
+	IPT_MULTIPORT_SOURCE = (1 << IPT_MULTIPORT_SOURCE_BIT),
+	
+	IPT_MULTIPORT_DESTINATION_BIT = 1,
+	IPT_MULTIPORT_DESTINATION = (1 << IPT_MULTIPORT_DESTINATION_BIT),
+
+	IPT_MULTIPORT_EITHER = IPT_MULTIPORT_SOURCE|IPT_MULTIPORT_DESTINATION,
 
 
                ^^^

flags like in ip_conntrack.h.

include/linux/netfilter_ipv4/ipt_multiport.h

Quoted 8 lines
include/linux/netfilter_ipv4/ipt_multiport.h +1 -1
 };
 
 #define IPT_MULTI_PORTS	15
 
-/* Must fit inside union ipt_matchinfo: 16 bytes */
+/* Must fit inside union ipt_matchinfo: 32 bytes */
 
 
    ^^^

Actually I just took this from mport, I found this Harald's email explaining this issue (actually I got stuck there some time because i didn't understand that comment :-))

http://lists.netfilter.org/pipermail/netfilter-devel/2003-January/010187.html

Quoted 15 lines
include/linux/netfilter_ipv4/ipt_multiport.h +6 -0
+/* every entry in ports[] except for the last one has one bit in pflags
+ * associated with it. If this bit is set, the port is the first port of
+ * a portrange, with the next entry being the last.
+ * End of list is marked with pflags bit set and port=65535.
+ * If 14 ports are used (last one does not have a pflag), the last port
+ * is repeated to fill the last entry in ports[] */

struct ipt_multiport
{

include/linux/netfilter_ipv4/ipt_multiport.h +2 -2
-	u_int8_t flags;				/* Type of comparison */
-	u_int8_t count;				/* Number of ports */
+	u_int8_t flags:2;			/* Type of comparison */
+	u_int16_t pflags:14;			/* Port flags */
	u_int16_t ports[IPT_MULTI_PORTS];	/* Ports */

include/linux/netfilter_ipv4/ipt_multiport.h line 29

Quoted 1 line
include/linux/netfilter_ipv4/ipt_multiport.h +1 -0
+	u_int8_t count;                         /* Number of ports */
                 ^^^

use count instead of 65535+flag as ending

Quoted 4 lines

};
#endif /*_IPT_MULTIPORT_H*/

regards,
Pablo

#2 lore raw

Hi Pablo,

Quoted 3 lines · Pablo Neira

Hi again Patrick,

I think that some comments about the patch could help you to review it, so here we go :-)

Thanks, I appreciate it :)

Quoted 9 lines
Quoted 9 lines

+/* every entry in ports[] except for the last one has one bit in pflags
+ * associated with it. If this bit is set, the port is the first port of
+ * a portrange, with the next entry being the last.
+ * End of list is marked with pflags bit set and port=65535.
+ * If 14 ports are used (last one does not have a pflag), the last port
+ * is repeated to fill the last entry in ports[] */
struct ipt_multiport
{

Quoted 8 lines

include/linux/netfilter_ipv4/ipt_multiport.h line 29

Quoted 8 lines
include/linux/netfilter_ipv4/ipt_multiport.h +3 -2
-    u_int8_t flags;                /* Type of comparison */
-    u_int8_t count;                /* Number of ports */
+    u_int8_t flags:2;            /* Type of comparison */
+    u_int16_t pflags:14;            /* Port flags */
    u_int16_t ports[IPT_MULTI_PORTS];    /* Ports */
+    u_int8_t count;                         /* Number of ports */

This breaks userspace compatibility. We can't do that, sorry.

Regards
Patrick

#3 lore raw

Hi Patrick,

Quoted 20 lines · Patrick McHardy
Quoted 11 lines
Quoted 11 lines

+/* every entry in ports[] except for the last one has one bit in pflags
+ * associated with it. If this bit is set, the port is the first port of
+ * a portrange, with the next entry being the last.
+ * End of list is marked with pflags bit set and port=65535.
+ * If 14 ports are used (last one does not have a pflag), the last port
+ * is repeated to fill the last entry in ports[] */ struct ipt_multiport
{

Quoted 8 lines

include/linux/netfilter_ipv4/ipt_multiport.h line 29

Quoted 8 lines
include/linux/netfilter_ipv4/ipt_multiport.h +3 -2
-    u_int8_t flags;                /* Type of comparison */
-    u_int8_t count;                /* Number of ports */
+    u_int8_t flags:2;            /* Type of comparison */
+    u_int16_t pflags:14;            /* Port flags */
    u_int16_t ports[IPT_MULTI_PORTS];    /* Ports */
+    u_int8_t count;                         /* Number of ports */
Quoted 1 line

This breaks userspace compatibility. We can't do that, sorry.

In that case, I have the patches without that change here, I'll test them and then I'll resend them to you again.

regards,
Pablo

#4 lore raw

Hi Patrick,

Quoted 1 line · Pablo Neira

In that case, I have the patches without that change here, I'll test them and then I'll resend them to you again.

Attached the new patches, they respect userspace compatibility. I tested them and they work for me.

- Iptables patch is mport version (by Andreas Feber) with some broken indentation fixed and a var renamed.
- Kernel patch is mport with flags defined in a different way.

regards,
Pablo

===== include/linux/netfilter_ipv4/ipt_multiport.h 1.1 vs edited =====

include/linux/netfilter_ipv4/ipt_multiport.h +16 -6
@@ -4,18 +4,28 @@
 
 enum ipt_multiport_flags
 {
-	IPT_MULTIPORT_SOURCE,
-	IPT_MULTIPORT_DESTINATION,
-	IPT_MULTIPORT_EITHER
+	IPT_MULTIPORT_SOURCE_BIT = 0,
+	IPT_MULTIPORT_SOURCE = (1 << IPT_MULTIPORT_SOURCE_BIT),
+
+	IPT_MULTIPORT_DESTINATION_BIT = 1,
+	IPT_MULTIPORT_DESTINATION = (1 << IPT_MULTIPORT_DESTINATION_BIT),
+
+	IPT_MULTIPORT_EITHER = IPT_MULTIPORT_SOURCE|IPT_MULTIPORT_DESTINATION,
 };
 
 #define IPT_MULTI_PORTS	15
 
-/* Must fit inside union ipt_matchinfo: 16 bytes */
+/* Must fit inside union ipt_matchinfo: 32 bytes */
+/* every entry in ports[] except for the last one has one bit in pflags
+ * associated with it. If this bit is set, the port is the first port of
+ * a portrange, with the next entry being the last.
+ * End of list is marked with pflags bit set and port=65535.
+ * If 14 ports are used (last one does not have a pflag), the last port
+ * is repeated to fill the last entry in ports[] */
 struct ipt_multiport
 {
-	u_int8_t flags;				/* Type of comparison */
-	u_int8_t count;				/* Number of ports */
+	u_int8_t flags:2;			/* Type of comparison */
+	u_int16_t pflags:14;			/* Port flags */
 	u_int16_t ports[IPT_MULTI_PORTS];	/* Ports */
 };
 #endif /*_IPT_MULTIPORT_H*/

===== net/ipv4/netfilter/ipt_multiport.c 1.8 vs edited =====

net/ipv4/netfilter/ipt_multiport.c +24 -17

73 lines not on this page · raw message

Index: extensions/libipt_multiport.c
===================================================================
RCS file: /cvspublic/iptables/extensions/libipt_multiport.c,v retrieving revision 1.8

libipt_multiport.c +61 -19

159 lines not on this page · raw message

#5 lore raw
Quoted 5 lines · Pablo Neira

Attached the new patches, they respect userspace compatibility. I tested them and they work for me.

- Iptables patch is mport version (by Andreas Feber) with some broken indentation fixed and a var renamed.
- Kernel patch is mport with flags defined in a different way.

Hm, but old binary with new kernel and new binary with old kernel compatibilities are still broken anyway... mport also modifies the layout... so putting multiport and mport together without breaking things is not obvious. Oh, time to sleep...

regards,
Pablo

#6 lore raw
In reply to Pablo Neira
Quoted 9 lines · Pablo Neira

Hi Patrick,

Attached a patch that puts mport and multiport together. It applies to latest bk snapshot.

I've also added some clean ups like the use of a count var inside the ipt_multiport instead of using 65535 and the pflag set to mark the end of the array of ports. I've tested and it works fine for me.

Quoted 5 lines

Also attached the patch for iptables and man page. If missing something, please let me know.

regards,
Pablo

Pablo -

first of all, thanks for doing this -- it will come in handy in my scripts.

Minor nit:

@@ -146,6 +161,10 @@

                exit_error(PARAMETER_PROBLEM,
                           "multiport does not support invert");

+ if (invert)
+ exit_error(PARAMETER_PROBLEM,
+ "multiport does not support invert");
+

        if (*flags)
                exit_error(PARAMETER_PROBLEM,
                           "multiport can only have one option");

this hunk looks redundant.

Phil

#7 lore raw

Hi Phil,

Quoted 14 lines · Phil Oester

Minor nit:

(quoted diff) +4 -0
@@ -146,6 +161,10 @@
               exit_error(PARAMETER_PROBLEM,
                          "multiport does not support invert");
 
+       if (invert)             
+               exit_error(PARAMETER_PROBLEM,
+                          "multiport does not support invert");
+       
       if (*flags)
               exit_error(PARAMETER_PROBLEM,
                          "multiport can only have one option");
Quoted 3 lines

this hunk looks redundant.

Hm I think that it got there while updating my cvs working copy. Thanks :-)

Attached a new version, if anything else, please let me know.

regards,
Pablo

Index: extensions/libipt_multiport.c
===================================================================
RCS file: /cvspublic/iptables/extensions/libipt_multiport.c,v retrieving revision 1.8

libipt_multiport.c +43 -18
diff -u -r1.8 libipt_multiport.c
@@ -13,13 +13,13 @@
 {
 	printf(
 "multiport v%s options:\n"
-" --source-ports port[,port,port...]\n"
+" --source-ports port[,port:port,port...]\n"
 " --sports ...\n"
 "				match source port(s)\n"
-" --destination-ports port[,port,port...]\n"
+" --destination-ports port[,port:port,port...]\n"
 " --dports ...\n"
 "				match destination port(s)\n"
-" --ports port[,port,port]\n"
+" --ports port[,port:port,port]\n"
 "				match both source and destination port(s)\n",
 IPTABLES_VERSION);
 }
@@ -57,24 +57,42 @@
 		   "invalid port/service `%s' specified", port);
 }
 
-static unsigned int
-parse_multi_ports(const char *portstring, u_int16_t *ports, const char *proto)
+static void
+parse_multi_ports(const char *portstring, struct ipt_multiport *multiinfo,
+                  const char *proto)
 {
-	char *buffer, *cp, *next;
+	char *buffer, *cp, *next, *range;
 	unsigned int i;
+	u_int16_t m;
 
 	buffer = strdup(portstring);
-	if (!buffer) exit_error(OTHER_PROBLEM, "strdup failed");
+	if (!buffer) exit_error(OTHER_PROBLEM, "strdup failed: OOM");
+
+	multiinfo->pflags = 0;
 
-	for (cp=buffer, i=0; cp && i<IPT_MULTI_PORTS; cp=next,i++)
-	{
+	for (cp=buffer, i=0, m=1; cp && i<IPT_MULTI_PORTS; cp=next,i++,m<<=1) {
 		next=strchr(cp, ',');
 		if (next) *next++='\0';
-		ports[i] = parse_port(cp, proto);
+		range = strchr(cp, ':');
+		if (range) {
+			if (i == IPT_MULTI_PORTS-1)
+				exit_error(PARAMETER_PROBLEM,
+					   "too many ports specified");
+			*range++ = '\0';
+		}
+		multiinfo->ports[i] = parse_port(cp, proto);
+		if (range) {
+			multiinfo->pflags |= m;
+			multiinfo->ports[++i] = parse_port(range, proto);
+			if (multiinfo->ports[i-1] >= multiinfo->ports[i])
+				exit_error(PARAMETER_PROBLEM,
+					   "invalid portrange specified");
+			m <<= 1;
+                }
 	}
+	multiinfo->count = i;
 	if (cp) exit_error(PARAMETER_PROBLEM, "too many ports specified");
 	free(buffer);
-	return i;
 }
 
 /* Initialize the match. */
@@ -114,8 +132,7 @@
 	case '1':
 		check_inverse(argv[optind-1], &invert, &optind, 0);
 		proto = check_proto(entry);
-		multiinfo->count = parse_multi_ports(argv[optind-1],
-						     multiinfo->ports, proto);
+		parse_multi_ports(argv[optind-1], multiinfo, proto);
 		multiinfo->flags = IPT_MULTIPORT_SOURCE;
 		*nfcache |= NFC_IP_SRC_PT;
 		break;
@@ -123,8 +140,7 @@
 	case '2':
 		check_inverse(argv[optind-1], &invert, &optind, 0);
 		proto = check_proto(entry);
-		multiinfo->count = parse_multi_ports(argv[optind-1],
-						     multiinfo->ports, proto);
+		parse_multi_ports(argv[optind-1], multiinfo, proto);
 		multiinfo->flags = IPT_MULTIPORT_DESTINATION;
 		*nfcache |= NFC_IP_DST_PT;
 		break;
@@ -132,8 +148,7 @@
 	case '3':
 		check_inverse(argv[optind-1], &invert, &optind, 0);
 		proto = check_proto(entry);
-		multiinfo->count = parse_multi_ports(argv[optind-1],
-						     multiinfo->ports, proto);
+		parse_multi_ports(argv[optind-1], multiinfo, proto);
 		multiinfo->flags = IPT_MULTIPORT_EITHER;
 		*nfcache |= NFC_IP_SRC_PT | NFC_IP_DST_PT;
 		break;
@@ -158,7 +173,7 @@
 final_check(unsigned int flags)
 {
 	if (!flags)
-		exit_error(PARAMETER_PROBLEM, "multiport expection an option");
+		exit_error(PARAMETER_PROBLEM, "multiport expects an option");
 }
 
 static char *
@@ -193,6 +208,7 @@
 	const struct ipt_multiport *multiinfo
 		= (const struct ipt_multiport *)match->data;
 	unsigned int i;
+	u_int16_t pflags = multiinfo->pflags;
 
 	printf("multiport ");
 
@@ -217,6 +233,10 @@
 	for (i=0; i < multiinfo->count; i++) {
 		printf("%s", i ? "," : "");
 		print_port(multiinfo->ports[i], ip->proto, numeric);
+		if (pflags & (1<<i)) {
+			printf(":");
+			print_port(multiinfo->ports[++i], ip->proto, numeric);
+		}
 	}
 	printf(" ");
 }
@@ -227,6 +247,7 @@
 	const struct ipt_multiport *multiinfo
 		= (const struct ipt_multiport *)match->data;
 	unsigned int i;
+	u_int16_t pflags = multiinfo->pflags;
 
 	switch (multiinfo->flags) {
 	case IPT_MULTIPORT_SOURCE:
@@ -245,6 +266,10 @@
 	for (i=0; i < multiinfo->count; i++) {
 		printf("%s", i ? "," : "");
 		print_port(multiinfo->ports[i], ip->proto, 1);
+                if (pflags & (1<<i)) {
+                        printf(":");
+                        print_port(multiinfo->ports[++i], ip->proto, 1);
+                }
 	}
 	printf(" ");
 }