tcpdump mpls pseudowire support

2016-07-07 Thread Rafael Zalamena
This diff teaches tcpdump to recognize MPLS pseudowires with control
words only. This should not be a problem since the control words are
used by default unless configured otherwise (ldpd does this).

It also makes possible to print encapsulated ethernet packets with the
new ethernet print function ether_tryprint(). Ethernet packets
encapsulated in MPLS pseudowires with no control words won't be shown,
because, as discussed previously, we have to make a lot of guesses to
find that out.

ok?

Note:
this is actually an updated diff of an old email I sent last year, the
differences between that and this are:
 * Use 'extern' in interface.h so the prototype looks like the others;
 * Re ordered the variable definition sequence;
 * print-mpls.c now uses the kernel headers to obtain the pseudowire
   macro definitions like: CW_ZERO_MASK and CW_FRAG_MASK;


Index: interface.h
===
RCS file: /cvs/src/usr.sbin/tcpdump/interface.h,v
retrieving revision 1.66
diff -u -p -r1.66 interface.h
--- interface.h 15 Nov 2015 20:35:36 -  1.66
+++ interface.h 7 Jul 2016 17:27:29 -
@@ -205,6 +205,7 @@ extern void pfsync_if_print(u_char *, co
 extern void pfsync_ip_print(const u_char *, u_int, const u_char *);
 extern void ether_if_print(u_char *, const struct pcap_pkthdr *,
const u_char *);
+extern void ether_tryprint(const u_char *, u_int, int);
 extern void fddi_if_print(u_char *, const struct pcap_pkthdr *, const u_char 
*);
 extern void ppp_ether_if_print(u_char *, const struct pcap_pkthdr *,
const u_char *);
Index: print-ether.c
===
RCS file: /cvs/src/usr.sbin/tcpdump/print-ether.c,v
retrieving revision 1.30
diff -u -p -r1.30 print-ether.c
--- print-ether.c   16 Nov 2015 00:16:39 -  1.30
+++ print-ether.c   7 Jul 2016 17:27:29 -
@@ -89,29 +89,34 @@ u_short extracted_ethertype;
 void
 ether_if_print(u_char *user, const struct pcap_pkthdr *h, const u_char *p)
 {
-   u_int caplen = h->caplen;
-   u_int length = h->len;
-   struct ether_header *ep;
-   u_short ether_type;
-
ts_print(>ts);
 
-   if (caplen < sizeof(struct ether_header)) {
-   printf("[|ether]");
-   goto out;
-   }
-
/*
 * Some printers want to get back at the ethernet addresses,
 * and/or check that they're not walking off the end of the packet.
 * Rather than pass them all the way down, we set these globals.
 */
-   packetp = p;
-   snapend = p + caplen;
+   snapend = p + h->caplen;
+
+   ether_tryprint(p, h->len, 1);
+}
+
+void
+ether_tryprint(const u_char *p, u_int length, int first_header)
+{
+   struct ether_header *ep;
+   u_int caplen = snapend - p;
+   u_short ether_type;
+
+   if (caplen < sizeof(struct ether_header)) {
+   printf("[|ether]");
+   goto out;
+   }
 
if (eflag)
ether_print(p, length);
 
+   packetp = p;
length -= sizeof(struct ether_header);
caplen -= sizeof(struct ether_header);
ep = (struct ether_header *)p;
@@ -152,14 +157,15 @@ ether_if_print(u_char *user, const struc
default_print(p, caplen);
}
}
-   if (xflag) {
+   if (xflag && first_header) {
if (eflag)
default_print(packetp, snapend - packetp);
else
default_print(p, caplen);
}
  out:
-   putchar('\n');
+   if (first_header)
+   putchar('\n');
 }
 
 /*
Index: print-mpls.c
===
RCS file: /cvs/src/usr.sbin/tcpdump/print-mpls.c,v
retrieving revision 1.2
diff -u -p -r1.2 print-mpls.c
--- print-mpls.c30 Jun 2010 19:01:06 -  1.2
+++ print-mpls.c7 Jul 2016 17:27:29 -
@@ -26,15 +26,24 @@
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
+#include 
+#include 
+#include 
+
 #include 
 
 #include "interface.h"
 #include "extract.h"   /* must come after interface.h */
 
+#define CW_SEQUENCE_MASK   (0xU)
+
+int controlword_tryprint(const u_char **, u_int *);
+
 void
 mpls_print(const u_char *bp, u_int len)
 {
u_int32_t tag, label, exp, bottom, ttl;
+   int has_cw;
 
  again:
if (bp + sizeof(tag) > snapend)
@@ -56,6 +65,9 @@ mpls_print(const u_char *bp, u_int len)
if (!bottom)
goto again;
 
+   /* Handle pseudowire control word. */
+   has_cw = controlword_tryprint(, );
+
/*
 * guessing the underlying protocol is about all we can do if
 * it's not explicitly defined.
@@ -107,7 +119,34 @@ mpls_print(const u_char *bp, u_int len)
}
}
 
+   if (has_cw)
+   ether_tryprint(bp, len, 0);
+
return;
 trunc:
printf("[|mpls]");
+}

Re: tcpdump mpls pseudowire support

2015-09-26 Thread Rafael Zalamena
On Fri, Jul 17, 2015 at 03:24:17PM -0300, Rafael Zalamena wrote:
> This diff adds support for detection of pseudowires inside of MPLS tagged
> packets. Basically it teaches MPLS to look for ethernet headers when there
> is no sign of IP headers.
> 
> --- SNIPPED OLD DIFF ---

This is an updated diff to teach tcpdump mpls parser to identify VPLS
packets and display the ethernet payload. This time it doesn't do any
clever tricks: it will only show VPLS encapsulated ethernet packet if
there is a control word (which is used by default in ldpd(8)).

ok?

Index: interface.h
===
RCS file: /cvs/src/usr.sbin/tcpdump/interface.h,v
retrieving revision 1.65
diff -u -p -r1.65 interface.h
--- interface.h 5 Apr 2015 17:02:57 -   1.65
+++ interface.h 26 Sep 2015 21:57:42 -
@@ -205,6 +205,7 @@ extern void pfsync_if_print(u_char *, co
 extern void pfsync_ip_print(const u_char *, u_int, const u_char *);
 extern void ether_if_print(u_char *, const struct pcap_pkthdr *,
const u_char *);
+void ether_tryprint(const u_char *, u_int, int);
 extern void fddi_if_print(u_char *, const struct pcap_pkthdr *, const u_char 
*);
 extern void ppp_ether_if_print(u_char *, const struct pcap_pkthdr *,
const u_char *);
Index: print-ether.c
===
RCS file: /cvs/src/usr.sbin/tcpdump/print-ether.c,v
retrieving revision 1.29
diff -u -p -r1.29 print-ether.c
--- print-ether.c   16 Jan 2015 06:40:21 -  1.29
+++ print-ether.c   26 Sep 2015 21:58:24 -
@@ -89,29 +89,34 @@ u_short extracted_ethertype;
 void
 ether_if_print(u_char *user, const struct pcap_pkthdr *h, const u_char *p)
 {
-   u_int caplen = h->caplen;
-   u_int length = h->len;
-   struct ether_header *ep;
-   u_short ether_type;
-
ts_print(>ts);
 
-   if (caplen < sizeof(struct ether_header)) {
-   printf("[|ether]");
-   goto out;
-   }
-
/*
 * Some printers want to get back at the ethernet addresses,
 * and/or check that they're not walking off the end of the packet.
 * Rather than pass them all the way down, we set these globals.
 */
-   packetp = p;
-   snapend = p + caplen;
+   snapend = p + h->caplen;
+
+   ether_tryprint(p, h->len, 1);
+}
+
+void
+ether_tryprint(const u_char *p, u_int length, int first_header)
+{
+   u_int caplen = snapend - p;
+   struct ether_header *ep;
+   u_short ether_type;
+
+   if (caplen < sizeof(struct ether_header)) {
+   printf("[|ether]");
+   goto out;
+   }
 
if (eflag)
ether_print(p, length);
 
+   packetp = p;
length -= sizeof(struct ether_header);
caplen -= sizeof(struct ether_header);
ep = (struct ether_header *)p;
@@ -152,14 +157,15 @@ ether_if_print(u_char *user, const struc
default_print(p, caplen);
}
}
-   if (xflag) {
+   if (xflag && first_header) {
if (eflag)
default_print(packetp, snapend - packetp);
else
default_print(p, caplen);
}
  out:
-   putchar('\n');
+   if (first_header)
+   putchar('\n');
 }
 
 /*
Index: print-mpls.c
===
RCS file: /cvs/src/usr.sbin/tcpdump/print-mpls.c,v
retrieving revision 1.2
diff -u -p -r1.2 print-mpls.c
--- print-mpls.c30 Jun 2010 19:01:06 -  1.2
+++ print-mpls.c26 Sep 2015 21:57:53 -
@@ -31,10 +31,17 @@
 #include "interface.h"
 #include "extract.h"   /* must come after interface.h */
 
+#define CW_ZERO_MASK   (0xf000U)
+#define CW_FRAG_MASK   (0x0fffU)
+#define CW_SEQUENCE_MASK   (0xU)
+
+int controlword_tryprint(const u_char **, u_int *);
+
 void
 mpls_print(const u_char *bp, u_int len)
 {
u_int32_t tag, label, exp, bottom, ttl;
+   int has_cw;
 
  again:
if (bp + sizeof(tag) > snapend)
@@ -56,6 +63,9 @@ mpls_print(const u_char *bp, u_int len)
if (!bottom)
goto again;
 
+   /* Handle pseudowire control word. */
+   has_cw = controlword_tryprint(, );
+
/*
 * guessing the underlying protocol is about all we can do if
 * it's not explicitly defined.
@@ -107,7 +117,34 @@ mpls_print(const u_char *bp, u_int len)
}
}
 
+   if (has_cw)
+   ether_tryprint(bp, len, 0);
+
return;
 trunc:
printf("[|mpls]");
+}
+
+/* Print control word if any and returns 1 on success. */
+int
+controlword_tryprint(const u_char **bp, u_int *lenp)
+{
+   u_int32_t cw, frag, seq;
+
+   if (*lenp < 4)
+   return (0);
+
+   cw = EXTRACT_32BITS(*bp);
+   if (cw & CW_ZERO_MASK)
+   return (0);

tcpdump mpls pseudowire support

2015-07-17 Thread Rafael Zalamena
This diff adds support for detection of pseudowires inside of MPLS tagged
packets. Basically it teaches MPLS to look for ethernet headers when there
is no sign of IP headers.

Index: interface.h
===
RCS file: /cvs/src/usr.sbin/tcpdump/interface.h,v
retrieving revision 1.65
diff -u -p -r1.65 interface.h
--- interface.h 5 Apr 2015 17:02:57 -   1.65
+++ interface.h 17 Jul 2015 18:16:43 -
@@ -205,6 +205,7 @@ extern void pfsync_if_print(u_char *, co
 extern void pfsync_ip_print(const u_char *, u_int, const u_char *);
 extern void ether_if_print(u_char *, const struct pcap_pkthdr *,
const u_char *);
+void ether_tryprint(const u_char *, u_int);
 extern void fddi_if_print(u_char *, const struct pcap_pkthdr *, const u_char 
*);
 extern void ppp_ether_if_print(u_char *, const struct pcap_pkthdr *,
const u_char *);
Index: print-ether.c
===
RCS file: /cvs/src/usr.sbin/tcpdump/print-ether.c,v
retrieving revision 1.29
diff -u -p -r1.29 print-ether.c
--- print-ether.c   16 Jan 2015 06:40:21 -  1.29
+++ print-ether.c   17 Jul 2015 18:16:43 -
@@ -89,29 +89,34 @@ u_short extracted_ethertype;
 void
 ether_if_print(u_char *user, const struct pcap_pkthdr *h, const u_char *p)
 {
-   u_int caplen = h-caplen;
-   u_int length = h-len;
-   struct ether_header *ep;
-   u_short ether_type;
-
ts_print(h-ts);
 
-   if (caplen  sizeof(struct ether_header)) {
-   printf([|ether]);
-   goto out;
-   }
-
/*
 * Some printers want to get back at the ethernet addresses,
 * and/or check that they're not walking off the end of the packet.
 * Rather than pass them all the way down, we set these globals.
 */
-   packetp = p;
-   snapend = p + caplen;
+   snapend = p + h-caplen;
+
+   ether_tryprint(p, h-len);
+}
+
+void
+ether_tryprint(const u_char *p, u_int length)
+{
+   u_int caplen = snapend - p;
+   struct ether_header *ep;
+   u_short ether_type;
+
+   if (caplen  sizeof(struct ether_header)) {
+   printf([|ether]);
+   goto out;
+   }
 
if (eflag)
ether_print(p, length);
 
+   packetp = p;
length -= sizeof(struct ether_header);
caplen -= sizeof(struct ether_header);
ep = (struct ether_header *)p;
Index: print-mpls.c
===
RCS file: /cvs/src/usr.sbin/tcpdump/print-mpls.c,v
retrieving revision 1.2
diff -u -p -r1.2 print-mpls.c
--- print-mpls.c30 Jun 2010 19:01:06 -  1.2
+++ print-mpls.c17 Jul 2015 18:16:43 -
@@ -31,6 +31,12 @@
 #include interface.h
 #include extract.h   /* must come after interface.h */
 
+#define CW_ZERO_MASK   (0xf000U)
+#define CW_FRAG_MASK   (0x0fffU)
+#define CW_SEQUENCE_MASK   (0xU)
+
+void controlword_print(const u_char **, u_int *);
+
 void
 mpls_print(const u_char *bp, u_int len)
 {
@@ -56,6 +62,9 @@ mpls_print(const u_char *bp, u_int len)
if (!bottom)
goto again;
 
+   /* Handle pseudowire control word if any. */
+   controlword_print(bp, len);
+
/*
 * guessing the underlying protocol is about all we can do if
 * it's not explicitly defined.
@@ -99,15 +108,48 @@ mpls_print(const u_char *bp, u_int len)
 
switch (bp[0]  0xf0) {
case 0x40:
+   /*
+* IPv4 second nibble is the header length and its
+* value must be at least 5 bytes long.
+*/
+   if ((bp[0]  0x0f)  5) {
+   ether_tryprint(bp, len);
+   break;
+   }
+
ip_print(bp, len);
break;
case 0x60:
ip6_print(bp, len);
break;
+   default:
+   ether_tryprint(bp, len);
+   break;
}
}
 
return;
 trunc:
printf([|mpls]);
+}
+
+void
+controlword_print(const u_char **bp, u_int *lenp)
+{
+   u_int32_t cw, frag, seq;
+
+   if (*lenp  4)
+   return;
+
+   cw = EXTRACT_32BITS(*bp);
+   if (cw  CW_ZERO_MASK)
+   return;
+
+   *bp += sizeof(cw);
+   *lenp += sizeof(cw);
+
+   frag = (cw  CW_FRAG_MASK)  16;
+   seq = cw  CW_SEQUENCE_MASK;
+
+   printf(CW(frag %u, sequence %u) , frag, seq);
 }



Re: tcpdump mpls pseudowire support

2015-07-17 Thread Claudio Jeker
On Fri, Jul 17, 2015 at 03:24:17PM -0300, Rafael Zalamena wrote:
 This diff adds support for detection of pseudowires inside of MPLS tagged
 packets. Basically it teaches MPLS to look for ethernet headers when there
 is no sign of IP headers.

The addition of controlword_print() is for sure OK.
Not super happy about the guesswork to decide if it is an ethernet frame
or not. Wonder if the presence of the controlword could be used to give a
better hint.
 
 Index: interface.h
 ===
 RCS file: /cvs/src/usr.sbin/tcpdump/interface.h,v
 retrieving revision 1.65
 diff -u -p -r1.65 interface.h
 --- interface.h   5 Apr 2015 17:02:57 -   1.65
 +++ interface.h   17 Jul 2015 18:16:43 -
 @@ -205,6 +205,7 @@ extern void pfsync_if_print(u_char *, co
  extern void pfsync_ip_print(const u_char *, u_int, const u_char *);
  extern void ether_if_print(u_char *, const struct pcap_pkthdr *,
   const u_char *);
 +void ether_tryprint(const u_char *, u_int);
  extern void fddi_if_print(u_char *, const struct pcap_pkthdr *, const u_char 
 *);
  extern void ppp_ether_if_print(u_char *, const struct pcap_pkthdr *,
   const u_char *);
 Index: print-ether.c
 ===
 RCS file: /cvs/src/usr.sbin/tcpdump/print-ether.c,v
 retrieving revision 1.29
 diff -u -p -r1.29 print-ether.c
 --- print-ether.c 16 Jan 2015 06:40:21 -  1.29
 +++ print-ether.c 17 Jul 2015 18:16:43 -
 @@ -89,29 +89,34 @@ u_short extracted_ethertype;
  void
  ether_if_print(u_char *user, const struct pcap_pkthdr *h, const u_char *p)
  {
 - u_int caplen = h-caplen;
 - u_int length = h-len;
 - struct ether_header *ep;
 - u_short ether_type;
 -
   ts_print(h-ts);
  
 - if (caplen  sizeof(struct ether_header)) {
 - printf([|ether]);
 - goto out;
 - }
 -
   /*
* Some printers want to get back at the ethernet addresses,
* and/or check that they're not walking off the end of the packet.
* Rather than pass them all the way down, we set these globals.
*/
 - packetp = p;
 - snapend = p + caplen;
 + snapend = p + h-caplen;
 +
 + ether_tryprint(p, h-len);
 +}
 +
 +void
 +ether_tryprint(const u_char *p, u_int length)
 +{
 + u_int caplen = snapend - p;
 + struct ether_header *ep;
 + u_short ether_type;
 +
 + if (caplen  sizeof(struct ether_header)) {
 + printf([|ether]);
 + goto out;
 + }
  
   if (eflag)
   ether_print(p, length);
  
 + packetp = p;
   length -= sizeof(struct ether_header);
   caplen -= sizeof(struct ether_header);
   ep = (struct ether_header *)p;
 Index: print-mpls.c
 ===
 RCS file: /cvs/src/usr.sbin/tcpdump/print-mpls.c,v
 retrieving revision 1.2
 diff -u -p -r1.2 print-mpls.c
 --- print-mpls.c  30 Jun 2010 19:01:06 -  1.2
 +++ print-mpls.c  17 Jul 2015 18:16:43 -
 @@ -31,6 +31,12 @@
  #include interface.h
  #include extract.h /* must come after interface.h */
  
 +#define CW_ZERO_MASK (0xf000U)
 +#define CW_FRAG_MASK (0x0fffU)
 +#define CW_SEQUENCE_MASK (0xU)
 +
 +void controlword_print(const u_char **, u_int *);
 +
  void
  mpls_print(const u_char *bp, u_int len)
  {
 @@ -56,6 +62,9 @@ mpls_print(const u_char *bp, u_int len)
   if (!bottom)
   goto again;
  
 + /* Handle pseudowire control word if any. */
 + controlword_print(bp, len);
 +
   /*
* guessing the underlying protocol is about all we can do if
* it's not explicitly defined.
 @@ -99,15 +108,48 @@ mpls_print(const u_char *bp, u_int len)
  
   switch (bp[0]  0xf0) {
   case 0x40:
 + /*
 +  * IPv4 second nibble is the header length and its
 +  * value must be at least 5 bytes long.
 +  */
 + if ((bp[0]  0x0f)  5) {
 + ether_tryprint(bp, len);
 + break;
 + }
 +
   ip_print(bp, len);
   break;
   case 0x60:
   ip6_print(bp, len);
   break;
 + default:
 + ether_tryprint(bp, len);
 + break;
   }
   }
  
   return;
  trunc:
   printf([|mpls]);
 +}
 +
 +void
 +controlword_print(const u_char **bp, u_int *lenp)
 +{
 + u_int32_t cw, frag, seq;
 +
 + if (*lenp  4)
 + return;
 +
 + cw = EXTRACT_32BITS(*bp);
 + if (cw  CW_ZERO_MASK)
 + return;
 +
 + *bp += sizeof(cw);
 + *lenp += sizeof(cw);
 +
 + frag = (cw  CW_FRAG_MASK)  16;
 + seq = cw  CW_SEQUENCE_MASK;
 +
 + printf(CW(frag %u,