match-1.3: add ipv4 src/dst tests
diff --git a/tests-1.3/match.py b/tests-1.3/match.py
index b0def5a..7650d8a 100644
--- a/tests-1.3/match.py
+++ b/tests-1.3/match.py
@@ -596,6 +596,151 @@
 
 # TODO IPv6 protocol (ICMP)
 
+class IPv4Src(MatchTest):
+    """
+    Match on ipv4 source address
+    """
+    def runTest(self):
+        match = ofp.match([
+            ofp.oxm.eth_type(0x0800),
+            ofp.oxm.ipv4_src(0xc0a80001), # 192.168.0.1
+        ])
+
+        matching = {
+            "192.168.0.1": simple_tcp_packet(ip_src='192.168.0.1'),
+        }
+
+        nonmatching = {
+            "192.168.0.2": simple_tcp_packet(ip_src='192.168.0.2'),
+            "255.255.255.255": simple_tcp_packet(ip_src='255.255.255.255'),
+        }
+
+        self.verify_match(match, matching, nonmatching)
+
+class IPv4SrcSubnetMasked(MatchTest):
+    """
+    Match on ipv4 source address (subnet masked)
+    """
+    def runTest(self):
+        match = ofp.match([
+            ofp.oxm.eth_type(0x0800),
+            # 192.168.0.0/20 (255.255.240.0)
+            ofp.oxm.ipv4_src_masked(0xc0a80000, 0xfffff000),
+        ])
+
+        matching = {
+            "192.168.0.1": simple_tcp_packet(ip_src='192.168.0.1'),
+            "192.168.0.2": simple_tcp_packet(ip_src='192.168.0.2'),
+            "192.168.4.2": simple_tcp_packet(ip_src='192.168.4.2'),
+            "192.168.0.0": simple_tcp_packet(ip_src='192.168.0.0'),
+            "192.168.15.255": simple_tcp_packet(ip_src='192.168.15.255'),
+        }
+
+        nonmatching = {
+            "192.168.16.0": simple_tcp_packet(ip_src='192.168.16.0'),
+            "192.167.255.255": simple_tcp_packet(ip_src='192.167.255.255'),
+            "192.168.31.1": simple_tcp_packet(ip_src='192.168.31.1'),
+        }
+
+        self.verify_match(match, matching, nonmatching)
+
+class IPv4SrcMasked(MatchTest):
+    """
+    Match on ipv4 source address (arbitrarily masked)
+    """
+    def runTest(self):
+        match = ofp.match([
+            ofp.oxm.eth_type(0x0800),
+            # 192.168.0.1 255.254.255.255
+            ofp.oxm.ipv4_src_masked(0xc0a80001, 0xfffeffff),
+        ])
+
+        matching = {
+            "192.168.0.1": simple_tcp_packet(ip_src='192.168.0.1'),
+            "192.169.0.1": simple_tcp_packet(ip_src='192.169.0.1'),
+        }
+
+        nonmatching = {
+            "192.168.0.2": simple_tcp_packet(ip_src='192.168.0.2'),
+            "192.167.0.1": simple_tcp_packet(ip_src='192.167.0.1'),
+        }
+
+        self.verify_match(match, matching, nonmatching)
+
+class IPv4Dst(MatchTest):
+    """
+    Match on ipv4 source address
+    """
+    def runTest(self):
+        match = ofp.match([
+            ofp.oxm.eth_type(0x0800),
+            ofp.oxm.ipv4_dst(0xc0a80001), # 192.168.0.1
+        ])
+
+        matching = {
+            "192.168.0.1": simple_tcp_packet(ip_dst='192.168.0.1'),
+        }
+
+        nonmatching = {
+            "192.168.0.2": simple_tcp_packet(ip_dst='192.168.0.2'),
+            "255.255.255.255": simple_tcp_packet(ip_dst='255.255.255.255'),
+        }
+
+        self.verify_match(match, matching, nonmatching)
+
+class IPv4DstSubnetMasked(MatchTest):
+    """
+    Match on ipv4 source address (subnet masked)
+    """
+    def runTest(self):
+        match = ofp.match([
+            ofp.oxm.eth_type(0x0800),
+            # 192.168.0.0/20 (255.255.240.0)
+            ofp.oxm.ipv4_dst_masked(0xc0a80000, 0xfffff000),
+        ])
+
+        matching = {
+            "192.168.0.1": simple_tcp_packet(ip_dst='192.168.0.1'),
+            "192.168.0.2": simple_tcp_packet(ip_dst='192.168.0.2'),
+            "192.168.4.2": simple_tcp_packet(ip_dst='192.168.4.2'),
+            "192.168.0.0": simple_tcp_packet(ip_dst='192.168.0.0'),
+            "192.168.15.255": simple_tcp_packet(ip_dst='192.168.15.255'),
+        }
+
+        nonmatching = {
+            "192.168.16.0": simple_tcp_packet(ip_dst='192.168.16.0'),
+            "192.167.255.255": simple_tcp_packet(ip_dst='192.167.255.255'),
+            "192.168.31.1": simple_tcp_packet(ip_dst='192.168.31.1'),
+        }
+
+        self.verify_match(match, matching, nonmatching)
+
+class IPv4DstMasked(MatchTest):
+    """
+    Match on ipv4 source address (arbitrarily masked)
+    """
+    def runTest(self):
+        match = ofp.match([
+            ofp.oxm.eth_type(0x0800),
+            # 192.168.0.1 255.254.255.255
+            ofp.oxm.ipv4_dst_masked(0xc0a80001, 0xfffeffff),
+        ])
+
+        matching = {
+            "192.168.0.1": simple_tcp_packet(ip_dst='192.168.0.1'),
+            "192.169.0.1": simple_tcp_packet(ip_dst='192.169.0.1'),
+        }
+
+        nonmatching = {
+            "192.168.0.2": simple_tcp_packet(ip_dst='192.168.0.2'),
+            "192.167.0.1": simple_tcp_packet(ip_dst='192.167.0.1'),
+        }
+
+        self.verify_match(match, matching, nonmatching)
+
+# TODO IPv6 source address
+# TODO IPv6 destination address
+
 class IPv4TCPSrc(MatchTest):
     """
     Match on ipv4 tcp source port