Ethereal-dev: [Ethereal-dev] [PATCH] pcap_set_datalink()/BIOCSDLT support

Note: This archive is from the project's previous web site, ethereal.com. This list is no longer active.

From: Brian Fundakowski Feldman <bfeldman@xxxxxxxxxxxxxxx>
Date: Fri, 31 Oct 2003 13:14:39 -0500
Since pcap(3) has recently grown the ability to select from multiple 
available data link types on some platforms, it's very useful for
Ethereal 
to support switching to a non-default capture format.  I've tested this
on 
FreeBSD, which means that the HAVE_PCAP_DATALINK_VAL_TO_NAME part
remains 
untested but present for a system which might have it.  After you select
an 
interface, you can check the "Change _data link type" box and then pull
down 
to select a data link type or enter your own (in my case, they are all 
numbers corresponding to the DLT_ values in the system headers).

The drop-down box isn't available (and the check box unchecks itself) if
the 
current value of the interface entry can't be used to look up a set of
data
link types.  The gtk_combo_set_value_in_list() should be removed if
someone 
sees a need to be able to enter a DLT number manually when the list of 
named DLT constants is available on a platform with 
pcap_datalink_val_to_name()... not sure if that would be remotely useful 
though :)  Does anyone have any thoughts?

-- 
Brian Fundakowski Feldman                    |     
http://www.fla.fujitsu.com/
Software Specialist                          |  Fujitsu Laboratories of
America
--- aclocal.m4	Wed Jul 23 18:37:22 2003
+++ ../ethereal-0.9.12/aclocal.m4	Mon Oct 27 14:14:12 2003
@@ -339,6 +339,25 @@
 	    AC_DEFINE(HAVE_LIBPCAP, 1, [Define to use libpcap library])
 	  ], [AC_MSG_ERROR(Library libpcap not found.)],
 	  $SOCKET_LIBS $NSL_LIBS)
+
+	#
+	# Check to see if we find "pcap_set_datalink" in "-lpcap".
+	#
+	AC_CHECK_LIB(pcap, pcap_set_datalink,
+	  [
+	    AC_DEFINE(HAVE_PCAP_SET_DATALINK, 1, [Define to use pcap multiple data link types])
+	  ], [AC_MSG_RESULT(Multiple data link types not required.)],
+	  $SOCKET_LIBS $NSL_LIBS $PCAP_LIBS)
+
+	#
+	# Check to see if we find "pcap_datalink_val_to_name" in "-lpcap".
+	#
+	AC_CHECK_LIB(pcap, pcap_datalink_val_to_name,
+	  [
+	    AC_DEFINE(HAVE_PCAP_DATALINK_VAL_TO_NAME, 1, [Define to use pcap data link name/type translation])
+	  ], [AC_MSG_RESULT(Data link name/type conversion not required.)],
+	  $SOCKET_LIBS $NSL_LIBS $PCAP_LIBS)
+
 	AC_SUBST(PCAP_LIBS)
 ])
 
--- capture.c.orig	Wed Jul 23 18:33:25 2003
+++ capture.c	Wed Oct 29 12:41:09 2003
@@ -393,6 +393,11 @@
       argv = add_arg(argv, &argc, ssnap);
     }
 
+    if (capture_opts.linktype) {
+      argv = add_arg(argv, &argc, "-y");
+      argv = add_arg(argv, &argc, capture_opts.linktype);
+    }
+
     if (capture_opts.has_autostop_filesize) {
       argv = add_arg(argv, &argc, "-a");
       sprintf(sautostop_filesize,"filesize:%d",capture_opts.autostop_filesize);
@@ -1578,6 +1583,15 @@
          error message from pcap_open_live() */
       open_err_str[0] = '\0';
 #endif
+  }
+
+  /* setting the data link type only works on real interfaces */
+  if (capture_opts.linktype) {
+    if (set_pcap_linktype(pch, cfile.iface, capture_opts.linktype) != 0) {
+      snprintf(errmsg, sizeof errmsg, "Unable to set %s pcap link type (%s).",
+	capture_opts.linktype, strerror(errno));
+      goto error;
+    }
   }
 
   /* capture filters only work on real interfaces */
--- capture.h.orig	Tue Oct 28 17:20:03 2003
+++ capture.h	Tue Oct 28 17:19:58 2003
@@ -51,6 +51,7 @@
 	guint32 ringbuffer_num_files;	/* Number of ring buffer files */
 	gboolean has_ring_duration;	/* TRUE if ring duration specified */
 	gint32 ringbuffer_duration;     /* Switch file after n seconds */
+	gchar *linktype;		/* String of data link type to use */
 } capture_options;
 
 extern capture_options capture_opts;
--- gtk/capture_dlg.c.orig	Sat May 17 12:33:02 2003
+++ gtk/capture_dlg.c	Wed Oct 29 12:48:55 2003
@@ -55,6 +55,8 @@
 /* Capture callback data keys */
 #define E_CAP_IFACE_KEY       "cap_iface"
 #define E_CAP_SNAP_CB_KEY     "cap_snap_cb"
+#define E_CAP_LT_CB_KEY       "cap_lt_cb"
+#define E_CAP_LT_SEL_KEY      "cap_lt_sel"
 #define E_CAP_SNAP_SB_KEY     "cap_snap_sb"
 #define E_CAP_PROMISC_KEY     "cap_promisc"
 #define E_CAP_FILT_KEY        "cap_filter_te"
@@ -104,6 +106,9 @@
 static void
 capture_prep_destroy_cb(GtkWidget *win, gpointer user_data);
 
+static void
+capture_prep_interface_changed_cb(GtkWidget *entry, gpointer parent_w);
+
 void
 capture_stop_cb(GtkWidget *w _U_, gpointer d _U_)
 {
@@ -124,6 +129,7 @@
   GtkWidget     *main_vb,
                 *capture_fr, *capture_vb,
                 *if_hb, *if_cb, *if_lb,
+                *linktype_hb, *linktype_cb, *linktype_sel,
                 *snap_hb, *snap_cb, *snap_sb, *snap_lb,
                 *promisc_cb,
                 *filter_hb, *filter_bt, *filter_te,
@@ -238,6 +244,29 @@
 
   free_interface_list(if_list);
 
+  /* Linktype row */
+  linktype_hb = gtk_hbox_new(FALSE, 3);
+  gtk_container_add(GTK_CONTAINER(capture_vb), linktype_hb);
+  gtk_widget_show(linktype_hb);
+
+#if GTK_MAJOR_VERSION < 2
+  linktype_cb = dlg_check_button_new_with_label_with_mnemonic(
+		"Change _data link type", accel_group);
+#else
+  linktype_cb = gtk_check_button_new_with_mnemonic("Change _data link type");
+#endif
+  gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(linktype_cb),
+		capture_opts.linktype != NULL);
+  SIGNAL_CONNECT(linktype_cb, "toggled", capture_prep_adjust_sensitivity, cap_open_w);
+  SIGNAL_CONNECT(GTK_ENTRY(GTK_COMBO(if_cb)->entry), "changed", capture_prep_interface_changed_cb, cap_open_w);
+  gtk_box_pack_start(GTK_BOX(linktype_hb), linktype_cb, FALSE, FALSE, 0);
+  gtk_widget_show(linktype_cb);
+
+  linktype_sel = gtk_combo_new();
+  gtk_combo_set_value_in_list(GTK_COMBO(linktype_sel), TRUE, TRUE);
+  gtk_box_pack_start (GTK_BOX(linktype_hb), linktype_sel, FALSE, FALSE, 0);
+  gtk_widget_show(linktype_sel);
+
   /* Capture length row */
   snap_hb = gtk_hbox_new(FALSE, 3);
   gtk_container_add(GTK_CONTAINER(capture_vb), snap_hb);
@@ -583,6 +612,8 @@
   OBJECT_SET_DATA(cap_open_w, E_CAP_IFACE_KEY, if_cb);
   OBJECT_SET_DATA(cap_open_w, E_CAP_SNAP_CB_KEY, snap_cb);
   OBJECT_SET_DATA(cap_open_w, E_CAP_SNAP_SB_KEY, snap_sb);
+  OBJECT_SET_DATA(cap_open_w, E_CAP_LT_CB_KEY, linktype_cb);
+  OBJECT_SET_DATA(cap_open_w, E_CAP_LT_SEL_KEY, linktype_sel);
   OBJECT_SET_DATA(cap_open_w, E_CAP_PROMISC_KEY, promisc_cb);
   OBJECT_SET_DATA(cap_open_w, E_CAP_FILT_KEY,  filter_te);
   OBJECT_SET_DATA(cap_open_w, E_CAP_FILE_TE_KEY,  file_te);
@@ -752,6 +783,7 @@
 capture_prep_ok_cb(GtkWidget *ok_bt _U_, gpointer parent_w) {
   GtkWidget *if_cb, *snap_cb, *snap_sb, *promisc_cb, *filter_te,
             *file_te, *ringbuffer_on_tb, *ringbuffer_nbf_sb,
+            *linktype_cb, *linktype_sel,
             *sync_cb, *auto_scroll_cb,
             *count_cb, *count_sb,
             *filesize_cb, *filesize_sb,
@@ -768,6 +800,8 @@
   if_cb     = (GtkWidget *) OBJECT_GET_DATA(parent_w, E_CAP_IFACE_KEY);
   snap_cb   = (GtkWidget *) OBJECT_GET_DATA(parent_w, E_CAP_SNAP_CB_KEY);
   snap_sb   = (GtkWidget *) OBJECT_GET_DATA(parent_w, E_CAP_SNAP_SB_KEY);
+  linktype_cb = (GtkWidget *) OBJECT_GET_DATA(parent_w, E_CAP_LT_CB_KEY);
+  linktype_sel = (GtkWidget *) OBJECT_GET_DATA(parent_w, E_CAP_LT_SEL_KEY);
   promisc_cb = (GtkWidget *) OBJECT_GET_DATA(parent_w, E_CAP_PROMISC_KEY);
   filter_te = (GtkWidget *) OBJECT_GET_DATA(parent_w, E_CAP_FILT_KEY);
   file_te   = (GtkWidget *) OBJECT_GET_DATA(parent_w, E_CAP_FILE_TE_KEY);
@@ -807,6 +841,15 @@
   cfile.iface = g_strdup(if_name);
   g_free(if_text);
 
+  if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(linktype_cb))) {
+    g_free(capture_opts.linktype);
+    capture_opts.linktype = g_strdup(gtk_entry_get_text(
+      GTK_ENTRY(GTK_COMBO(linktype_sel)->entry)));
+  } else {
+    g_free(capture_opts.linktype);
+    capture_opts.linktype = NULL;
+  }
+
   capture_opts.has_snaplen =
     gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(snap_cb));
   if (capture_opts.has_snaplen) {
@@ -943,6 +986,22 @@
   cap_open_w = NULL;
 }
 
+static void
+capture_prep_interface_changed_cb(GtkWidget *entry _U_, gpointer parent_w)
+{
+  GtkWidget *linktype_cb;
+
+  linktype_cb = OBJECT_GET_DATA(parent_w, E_CAP_LT_CB_KEY);
+  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(linktype_cb), FALSE);
+}
+
+static void
+do_g_free(gpointer tofree, gpointer dontfree _U_)
+{
+
+  g_free(tofree);
+}
+
 /*
  * Adjust the sensitivity of various widgets as per the current setting
  * of other widgets.
@@ -950,14 +1009,17 @@
 static void
 capture_prep_adjust_sensitivity(GtkWidget *tb _U_, gpointer parent_w)
 {
-  GtkWidget *snap_cb, *snap_sb,
+  GtkWidget *if_cb,
+            *snap_cb, *snap_sb,
             *ringbuffer_on_tb, *ringbuffer_nbf_lb, *ringbuffer_nbf_sb,
             *sync_cb, *auto_scroll_cb,
             *count_cb, *count_sb,
             *filesize_cb, *filesize_sb, *filesize_lb,
 	    *duration_cb, *duration_sb,
-	    *ring_duration_cb, *ring_duration_sb;
+	    *ring_duration_cb, *ring_duration_sb,
+            *linktype_cb, *linktype_sel;
 
+  if_cb = (GtkWidget *) OBJECT_GET_DATA(parent_w, E_CAP_IFACE_KEY);
   snap_cb = (GtkWidget *) OBJECT_GET_DATA(parent_w, E_CAP_SNAP_CB_KEY);
   snap_sb = (GtkWidget *) OBJECT_GET_DATA(parent_w, E_CAP_SNAP_SB_KEY);
   ringbuffer_on_tb  = (GtkWidget *) OBJECT_GET_DATA(parent_w, E_CAP_RING_ON_TB_KEY);
@@ -974,6 +1036,39 @@
   filesize_lb = (GtkWidget *) OBJECT_GET_DATA(parent_w, E_CAP_FILESIZE_LB_KEY);
   duration_cb = (GtkWidget *) OBJECT_GET_DATA(parent_w, E_CAP_DURATION_CB_KEY);
   duration_sb = (GtkWidget *) OBJECT_GET_DATA(parent_w, E_CAP_DURATION_SB_KEY);
+  linktype_cb = (GtkWidget *) OBJECT_GET_DATA(parent_w, E_CAP_LT_CB_KEY);
+  linktype_sel = (GtkWidget *) OBJECT_GET_DATA(parent_w, E_CAP_LT_SEL_KEY);
+
+  if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(linktype_cb))) {
+    gchar *curif;
+
+    curif = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(if_cb)->entry));
+    /* Windows uses /^.* ifname$/ */
+    if (strrchr(curif, ' ') != NULL)
+      curif = strrchr(curif, ' ') + 1;
+    if (curif != NULL) {
+      GList *lt_list;
+
+      lt_list = get_pcap_linktype_list(curif);
+      if (lt_list == NULL) {
+        gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(linktype_cb), FALSE);
+      } else {
+        gtk_combo_set_popdown_strings(GTK_COMBO(linktype_sel), lt_list);
+        gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(linktype_sel)->entry),
+	    g_list_nth_data(lt_list, 0));
+	g_list_foreach(lt_list, do_g_free, NULL);
+	g_list_free(lt_list);
+      }
+    } else {
+      gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(linktype_cb), FALSE);
+    }
+  }
+  if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(linktype_cb))) {
+    gtk_widget_set_sensitive(GTK_WIDGET(linktype_sel), TRUE);
+  } else {
+    gtk_widget_set_sensitive(GTK_WIDGET(linktype_sel), FALSE);
+    gtk_list_clear_items(GTK_LIST(GTK_COMBO(linktype_sel)->list), 0, -1);
+  }
 
   /* The snapshot length spinbox is sensitive iff the "Limit each packet
      to" checkbox is on. */
--- gtk/main.c.orig	Wed Oct 29 12:43:26 2003
+++ gtk/main.c	Wed Oct 29 12:47:54 2003
@@ -1187,7 +1187,7 @@
   fprintf(stderr, "\t[ -o <preference setting> ] ... [ -P <packet list height> ]\n");
   fprintf(stderr, "\t[ -r <infile> ] [ -R <read filter> ] [ -s <snaplen> ] \n");
   fprintf(stderr, "\t[ -t <time stamp format> ] [ -T <tree view height> ]\n");
-  fprintf(stderr, "\t[ -w <savefile> ] [ <infile> ]\n");
+  fprintf(stderr, "\t[ -w <savefile> ] [ -y <linktype> ] [ <infile> ]\n");
 #else
   fprintf(stderr, "\n%s [ -vh ] [ -n ] [ -B <byte view height> ] [ -m <medium font> ]\n",
 	  PACKAGE);
@@ -1473,7 +1473,7 @@
   ethereal_tap_list   *tli = NULL;
   gchar               *tap_opt = NULL;
 
-#define OPTSTRING_INIT "a:b:B:c:f:hi:klm:nN:o:pP:Qr:R:Ss:t:T:w:vz:"
+#define OPTSTRING_INIT "a:b:B:c:f:hi:klm:nN:o:pP:Qr:R:Ss:t:T:w:vy:z:"
 
 #ifdef HAVE_LIBPCAP
 #ifdef WIN32
@@ -1616,6 +1616,7 @@
   capture_opts.ringbuffer_num_files = RINGBUFFER_MIN_NUM_FILES;
   capture_opts.has_ring_duration = FALSE;
   capture_opts.ringbuffer_duration = 1;
+  capture_opts.linktype = NULL;
 
   /* If this is a capture child process, it should pay no attention
      to the "prefs.capture_prom_mode" setting in the preferences file;
@@ -1866,6 +1867,14 @@
         arg_error = TRUE;
 #endif
 	break;
+      case 'y':        /* Set the pcap data link type */
+#ifdef HAVE_LIBPCAP
+        capture_opts.linktype = g_strdup(optarg);
+#else
+        capture_option_specified = TRUE;
+        arg_error = TRUE;
+#endif
+        break;
 #ifdef HAVE_LIBPCAP
       /* This is a hidden option supporting Sync mode, so we don't set
        * the error flags for the user in the non-libpcap case.
--- pcap-util.c	Wed Jul  9 21:20:13 2003
+++ ../ethereal-0.9.12/pcap-util.c	Tue Oct 28 16:16:20 2003
@@ -31,6 +31,7 @@
 #include <glib.h>
 
 #include <stdlib.h>
+#include <limits.h>
 #include <string.h>
 #include <stdio.h>
 #include <errno.h>
@@ -194,6 +195,87 @@
 }
 
 /*
+ * Get the data-link types available for a libpcap device.
+ */
+GList *
+get_pcap_linktype_list(char *devname)
+{
+	GList *linktype_list = NULL;
+	pcap_t *pch;
+	char err_buf[PCAP_ERRBUF_SIZE];
+#ifdef HAVE_PCAP_SET_DATALINK
+	int *linktypes;
+	int i, nlt, deflt;
+#endif
+
+	pch = pcap_open_live(devname, MIN_PACKET_SIZE, 0, 0, err_buf);
+	if (pch == NULL)
+		return NULL;
+	deflt = get_pcap_linktype(pch, devname);
+	/* TODO: Map link numbers to names (pcap_datalink_val_to_name). */
+#ifdef HAVE_PCAP_SET_DATALINK
+	nlt = pcap_list_datalinks(pch, &linktypes);
+	if (nlt > 0) {
+		for (i = 0; i < nlt; i++) {
+			char *typename;
+#ifdef HAVE_PCAP_DATALINK_VAL_TO_NAME
+			typename = pcap_datalink_val_to_name(linktypes[i]);
+			if (typename != NULL)
+				typename = g_strdup(typename));
+			else
+#endif
+				typename = g_strdup_printf("%d", linktypes[i]);
+			if (linktypes[i] == deflt)
+				linktype_list = g_list_prepend(linktype_list,
+				    typename);
+			else
+				linktype_list = g_list_append(linktype_list,
+				    typename);
+		}
+		free(linktypes);
+	} else
+#endif
+		linktype_list = g_list_append(linktype_list,
+		    g_strdup_printf("%d (not modifiable)",
+			get_pcap_linktype(pch, devname)));
+
+	pcap_close(pch);
+	return linktype_list;
+}
+
+/* Set the data link type on a pcap, including name/val translation. */
+int
+set_pcap_linktype(pcap_t *pch, char *devname, char *linktype)
+{
+	int val;
+
+#ifdef HAVE_PCAP_DATALINK_VAL_TO_NAME
+	val = pcap_datalink_name_to_val(linktype);
+	if (val == -1) {
+#endif
+		long lval;
+		char *endptr;
+
+		lval = strtol(linktype, &endptr, 10);
+		if (*endptr != '\0' || lval < INT_MIN || lval > INT_MAX) {
+			errno = EINVAL;
+			return -1;
+		}
+		val = (int)lval;
+#ifdef HAVE_PCAP_DATALINK_VAL_TO_NAME
+	}
+#endif
+#ifdef HAVE_PCAP_SET_DATALINK
+	return (pcap_set_datalink(pch, val));
+#else
+	if (get_pcap_linktype(pch, devname) == val)
+		return 0;
+	errno = EINVAL;
+	return -1;
+#endif
+}
+
+/*
  * If the ability to capture packets is added to Wiretap, these
  * routines should be moved to the Wiretap source (with
  * "get_interface_list()" and "free_interface_list()" renamed to
--- pcap-util.h	Wed Aug 28 20:40:05 2002
+++ ../ethereal-0.9.12/pcap-util.h	Mon Oct 27 16:39:42 2003
@@ -32,6 +32,8 @@
 #endif /* __cplusplus */
 
 int get_pcap_linktype(pcap_t *pch, char *devname);
+GList *get_pcap_linktype_list(char *devname);
+int set_pcap_linktype(pcap_t *pch, char *devname, gchar *linktype);
 
 GList *get_interface_list(int *err, char *err_str);
 
--- tethereal.c.orig	Wed Oct 29 12:36:36 2003
+++ tethereal.c	Wed Oct 29 13:03:07 2003
@@ -192,6 +192,7 @@
 	guint32 ringbuffer_num_files;	/* Number of ring buffer files */
 	gboolean has_ring_duration;	/* TRUE if ring duration specified */
 	gint32 ringbuffer_duration;     /* Switch file after n seconds */
+	gchar *linktype;		/* String of data link type to use */
 } capture_options;
 
 static capture_options capture_opts = {
@@ -210,7 +211,8 @@
 	RINGBUFFER_MIN_NUM_FILES,	/* default number of ring buffer
 					   files */
 	FALSE,				/* Switch ring file after some */
-	0				/* specified time is off by default */
+	0,				/* specified time is off by default */
+	NULL				/* Default to not change link type */
 };
 
 #ifdef SIGINFO
@@ -236,7 +238,7 @@
   fprintf(stderr, "\t[ -f <capture filter> ] [ -F <output file type> ] [ -i <interface> ]\n");
   fprintf(stderr, "\t[ -N <resolving> ] [ -o <preference setting> ] ... [ -r <infile> ]\n");
   fprintf(stderr, "\t[ -R <read filter> ] [ -s <snaplen> ] [ -t <time stamp format> ]\n");
-  fprintf(stderr, "\t[ -w <savefile> ] [ -Z <statistics string> ]\n");
+  fprintf(stderr, "\t[ -w <savefile> ] [ -y <link type> ] [ -Z <statistics string> ]\n");
 #else
   fprintf(stderr, "\nt%s [ -vh ] [ -lnVx ]\n", PACKAGE);
   fprintf(stderr, "\t[ -d %s ] ...\n", decode_as_arg_template);
@@ -841,7 +843,7 @@
   get_runtime_version_info(runtime_info_str);
 
   /* Now get our args */
-  while ((opt = getopt(argc, argv, "a:b:c:d:Df:F:hi:lnN:o:pqr:R:s:St:vw:Vxz:")) != -1) {
+  while ((opt = getopt(argc, argv, "a:b:c:d:Df:F:hi:lnN:o:pqr:R:s:St:vw:Vxy:z:")) != -1) {
     switch (opt) {
       case 'a':        /* autostop criteria */
 #ifdef HAVE_LIBPCAP
@@ -1038,6 +1040,14 @@
         break;
       case 'x':        /* Print packet data in hex (and ASCII) */
         print_hex = TRUE;
+        break;
+      case 'y':        /* Set the pcap data link type */
+#ifdef HAVE_LIBPCAP
+        capture_opts.linktype = g_strdup(optarg);
+#else
+        capture_option_specified = TRUE;
+        arg_error = TRUE;
+#endif
         break;
       case 'z':
         for(tli=tap_list;tli;tli=tli->next){