diff --git a/brctl/brctl_cmd.c b/brctl/brctl_cmd.c
index c93dd55..d37e99c 100644
--- a/brctl/brctl_cmd.c
+++ b/brctl/brctl_cmd.c
@@ -395,6 +395,42 @@ static int br_cmd_showmacs(int argc, char *const* argv)
 	return 0;
 }
 
+static int br_cmd_hairpin(int argc, char *const* argv)
+{
+	int hairpin, err;
+	const char *brname = *++argv;
+	const char *ifname = *++argv;
+	const char *hpmode = *++argv;
+
+	if (!strcmp(hpmode, "on") || !strcmp(hpmode, "yes")
+	    || !strcmp(hpmode, "1"))
+		hairpin = 1;
+	else if (!strcmp(hpmode, "off") || !strcmp(hpmode, "no")
+		 || !strcmp(hpmode, "0"))
+		hairpin = 0;
+	else {
+		fprintf(stderr, "expect on/off for argument\n");
+		return 1;
+	}
+	if (if_nametoindex(ifname) == 0) {
+		fprintf(stderr, "interface %s does not exist!\n",
+			ifname);
+		return 1;
+	} else if (if_nametoindex(brname) == 0) {
+		fprintf(stderr, "bridge %s does not exist!\n",
+			brname);
+		return 1;
+	}
+
+	err = br_set_hairpin_mode(brname, ifname, hairpin);
+
+	if (err) {
+		fprintf(stderr, "can't set %s to hairpin on bridge %s: %s\n",
+			ifname, brname, strerror(err));
+	}
+	return err != 0;
+}
+
 static const struct command commands[] = {
 	{ 1, "addbr", br_cmd_addbr, "<bridge>\t\tadd bridge" },
 	{ 1, "delbr", br_cmd_delbr, "<bridge>\t\tdelete bridge" },
@@ -402,6 +438,8 @@ static const struct command commands[] = {
 	  "<bridge> <device>\tadd interface to bridge" },
 	{ 2, "delif", br_cmd_delif,
 	  "<bridge> <device>\tdelete interface from bridge" },
+	{ 3, "hairpin", br_cmd_hairpin,
+	  "<bridge> <port> {on|off}\tturn hairpin on/off" },
 	{ 2, "setageing", br_cmd_setageing,
 	  "<bridge> <time>\t\tset ageing time" },
 	{ 2, "setbridgeprio", br_cmd_setbridgeprio,
diff --git a/brctl/brctl_disp.c b/brctl/brctl_disp.c
index 27ce6d2..3e81241 100644
--- a/brctl/brctl_disp.c
+++ b/brctl/brctl_disp.c
@@ -93,6 +93,8 @@ static int dump_port_info(const char *br, const char *p,  void *arg)
 		printf("CONFIG_PENDING ");
 	if (pinfo.top_change_ack)
 		printf("TOPOLOGY_CHANGE_ACK ");
+	if (pinfo.hairpin_mode)
+		printf("\n hairpin mode\t\t\%4i", pinfo.hairpin_mode);
 	printf("\n");
 	printf("\n");
 	return 0;
diff --git a/libbridge/libbridge.h b/libbridge/libbridge.h
index 016acea..39964f2 100644
--- a/libbridge/libbridge.h
+++ b/libbridge/libbridge.h
@@ -80,6 +80,7 @@ struct port_info
 	struct timeval message_age_timer_value;
 	struct timeval forward_delay_timer_value;
 	struct timeval hold_timer_value;
+	unsigned char hairpin_mode;
 };
 
 extern int br_init(void);
@@ -113,4 +114,6 @@ extern int br_set_path_cost(const char *br, const char *p,
 			    int path_cost);
 extern int br_read_fdb(const char *br, struct fdb_entry *fdbs, 
 		       unsigned long skip, int num);
+extern int br_set_hairpin_mode(const char *bridge, const char *dev,
+			       int hairpin_mode);
 #endif
diff --git a/libbridge/libbridge_devif.c b/libbridge/libbridge_devif.c
index 34e3cc8..ca40325 100644
--- a/libbridge/libbridge_devif.c
+++ b/libbridge/libbridge_devif.c
@@ -36,6 +36,14 @@ static FILE *fpopen(const char *dir, const char *name)
 	return fopen(path, "r");
 }
 
+static int fpaccess(const char *dir, const char *name)
+{
+	char path[SYSFS_PATH_MAX];
+
+	snprintf(path, SYSFS_PATH_MAX, "%s/%s", dir, name);
+	return access(path, F_OK);
+}
+
 static void fetch_id(const char *dev, const char *name, struct bridge_id *id)
 {
 	FILE *f = fpopen(dev, name);
@@ -73,6 +81,12 @@ static void fetch_tv(const char *dev, const char *name,
 	__jiffies_to_tv(tv, fetch_int(dev, name));
 }
 
+/* Check if a feature is supported. */
+static int feature_supported(const char *dev, const char *feature)
+{
+	return !fpaccess(dev, feature);
+}
+
 /*
  * Convert device name to an index in the list of ports in bridge.
  *
@@ -239,6 +253,7 @@ static int old_get_port_info(const char *brname, const char *port,
 	__jiffies_to_tv(&info->forward_delay_timer_value, 
 			i.forward_delay_timer_value);
 	__jiffies_to_tv(&info->hold_timer_value, i.hold_timer_value);
+	info->hairpin_mode = 0;
 	return 0;
 }
 
@@ -271,6 +286,10 @@ int br_get_port_info(const char *brname, const char *port,
 	fetch_tv(path, "message_age_timer", &info->message_age_timer_value);
 	fetch_tv(path, "forward_delay_timer", &info->forward_delay_timer_value);
 	fetch_tv(path, "hold_timer", &info->hold_timer_value);
+	if (feature_supported(path, "hairpin_mode"))
+		info->hairpin_mode = fetch_int(path, "hairpin_mode");
+	else
+		info->hairpin_mode = 0;
 	closedir(d);
 
 	return 0;
@@ -381,6 +400,11 @@ int br_set_path_cost(const char *bridge, const char *port, int cost)
 	return port_set(bridge, port, "path_cost", cost, BRCTL_SET_PATH_COST);
 }
 
+int br_set_hairpin_mode(const char *bridge, const char *port, int hairpin_mode)
+{
+	return port_set(bridge, port, "hairpin_mode", hairpin_mode, 0);
+}
+
 static inline void __copy_fdb(struct fdb_entry *ent, 
 			      const struct __fdb_entry *f)
 {
