diff --git a/lib/mac-learning.c b/lib/mac-learning.c
index 9442858d9..ff21289e8 100644
--- a/lib/mac-learning.c
+++ b/lib/mac-learning.c
@@ -39,8 +39,13 @@ COVERAGE_DEFINE(mac_learning_moved);
 int
 mac_entry_age(const struct mac_learning *ml, const struct mac_entry *e)
 {
-    time_t remaining = e->expires - time_now();
-    return ml->idle_time - remaining;
+    /* For static fdb entries, expires would be initialized with INT_MAX */
+    if (INT_MAX == e->expires) {
+        return -1;
+    } else {
+        time_t remaining = e->expires - time_now();
+        return ml->idle_time - remaining;
+    }
 }
 
 static uint32_t
@@ -282,6 +287,18 @@ mac_learning_set_idle_time(struct mac_learning *ml, unsigned int idle_time)
     }
 }
 
+/* Changes the MAC aging timeout of a mac_entry to 'idle_time' seconds. */
+void
+mac_entry_set_idle_time(struct mac_learning *ml, struct eth_addr mac,
+        int vlan, unsigned int idle_time)
+{
+    struct mac_entry *e;
+    e = mac_entry_lookup(ml, mac, vlan);
+    if (e) {
+        e->expires = idle_time;
+    }
+}
+
 /* Sets the maximum number of entries in 'ml' to 'max_entries', adjusting it
  * to be within a reasonable range. */
 void
@@ -348,7 +365,10 @@ mac_learning_insert(struct mac_learning *ml,
         ovs_list_remove(&e->port_lru_node);
         ovs_list_push_back(&e->mlport->port_lrus, &e->port_lru_node);
     }
-    e->expires = time_now() + ml->idle_time;
+    /* Do not update 'expires' for static mac entry */
+    if (e->expires != INT_MAX) {
+        e->expires = time_now() + ml->idle_time;
+    }
 
     return e;
 }
diff --git a/lib/mac-learning.h b/lib/mac-learning.h
index 0ddab06cb..1137aae63 100644
--- a/lib/mac-learning.h
+++ b/lib/mac-learning.h
@@ -202,6 +202,9 @@ bool mac_learning_set_flood_vlans(struct mac_learning *ml,
 void mac_learning_set_idle_time(struct mac_learning *ml,
                                 unsigned int idle_time)
     OVS_REQ_WRLOCK(ml->rwlock);
+void mac_entry_set_idle_time(struct mac_learning *ml, struct eth_addr src,
+                             int vlan, unsigned int idle_time)
+    OVS_REQ_WRLOCK(ml->rwlock);
 void mac_learning_set_max_entries(struct mac_learning *ml, size_t max_entries)
     OVS_REQ_WRLOCK(ml->rwlock);
 
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index 7108c8a30..f1072234e 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -8011,6 +8011,31 @@ xlate_mac_learning_update(const struct ofproto_dpif *ofproto,
     update_learning_table__(xbridge, xbundle, dl_src, vlan, is_grat_arp);
 }
 
+void
+xlate_add_static_mac_entry(const struct ofproto_dpif *ofproto, ofp_port_t in_port,
+                           struct eth_addr dl_src, int vlan)
+{
+    xlate_mac_learning_update(ofproto, in_port, dl_src, vlan, false);
+
+    ovs_rwlock_wrlock(&ofproto->ml->rwlock);
+    mac_entry_set_idle_time(ofproto->ml, dl_src, vlan, INT_MAX);
+    ovs_rwlock_unlock(&ofproto->ml->rwlock);
+}
+
+void
+xlate_delete_static_mac_entry(const struct ofproto_dpif *ofproto,
+                              struct eth_addr dl_src, int vlan)
+{
+    struct mac_entry *e = NULL;
+
+    ovs_rwlock_wrlock(&ofproto->ml->rwlock);
+    e = mac_learning_lookup(ofproto->ml, dl_src, vlan);
+    if (e) {
+        mac_learning_expire(ofproto->ml, e);
+    }
+    ovs_rwlock_unlock(&ofproto->ml->rwlock);
+}
+
 void
 xlate_set_support(const struct ofproto_dpif *ofproto,
                     const struct dpif_backer_support *support)
diff --git a/ofproto/ofproto-dpif-xlate.h b/ofproto/ofproto-dpif-xlate.h
index 3426a27b2..ca816e645 100644
--- a/ofproto/ofproto-dpif-xlate.h
+++ b/ofproto/ofproto-dpif-xlate.h
@@ -225,6 +225,10 @@ int xlate_send_packet(const struct ofport_dpif *, bool oam, struct dp_packet *);
 void xlate_mac_learning_update(const struct ofproto_dpif *ofproto,
                                ofp_port_t in_port, struct eth_addr dl_src,
                                int vlan, bool is_grat_arp);
+void xlate_add_static_mac_entry(const struct ofproto_dpif *, ofp_port_t in_port,
+                               struct eth_addr dl_src, int vlan);
+void xlate_delete_static_mac_entry(const struct ofproto_dpif *,
+                                  struct eth_addr dl_src, int vlan);
 
 void xlate_set_support(const struct ofproto_dpif *,
                        const struct dpif_backer_support *);
diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
index fd0b2fdea..7f696481f 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -5864,6 +5864,48 @@ ofproto_unixctl_fdb_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
     ds_destroy(&ds);
 }
 
+static void
+ofproto_unixctl_fdb_update(struct unixctl_conn *conn, int argc OVS_UNUSED,
+                         const char *argv[], void *aux OVS_UNUSED)
+{
+    const char *br_name = argv[1];
+    const char *name = argv[2];
+    struct eth_addr mac;
+    uint16_t vlan = atoi(argv[3]);
+    const char *op = (const char *)aux;
+    const struct ofproto_dpif *ofproto;
+    ofp_port_t    in_port = OFPP_NONE;
+    struct ofproto_port ofproto_port;
+
+    ofproto = ofproto_dpif_lookup_by_name(br_name);
+    if (!ofproto) {
+        unixctl_command_reply_error(conn, "no such bridge");
+        return;
+    }
+
+    if (!eth_addr_from_string(argv[4], &mac)) {
+        unixctl_command_reply_error(conn, "bad MAC address");
+        return;
+    }
+
+    if (ofproto_port_query_by_name(&ofproto->up, name, &ofproto_port)) {
+        unixctl_command_reply_error(conn, "software error, odp port is present but no ofp port");
+        return;
+    }
+    in_port = ofproto_port.ofp_port;
+    ofproto_port_destroy(&ofproto_port);
+
+    if (!strcmp(op, "add")) {
+        xlate_add_static_mac_entry(ofproto, in_port, mac, vlan);
+        unixctl_command_reply(conn, "OK");
+    } else if (!strcmp(op, "del")) {
+        xlate_delete_static_mac_entry(ofproto, mac, vlan);
+        unixctl_command_reply(conn, "OK");
+    } else {
+        unixctl_command_reply_error(conn, "software error, unknown op");
+    }
+}
+
 static void
 ofproto_unixctl_fdb_stats_clear(struct unixctl_conn *conn, int argc,
                                 const char *argv[], void *aux OVS_UNUSED)
@@ -6415,6 +6457,10 @@ ofproto_unixctl_init(void)
     }
     registered = true;
 
+    unixctl_command_register("fdb/add", "[bridge port vlan mac]", 4, 4,
+                             ofproto_unixctl_fdb_update, "add");
+    unixctl_command_register("fdb/del", "[bridge port vlan mac]", 4, 4,
+                             ofproto_unixctl_fdb_update, "del");
     unixctl_command_register("fdb/flush", "[bridge]", 0, 1,
                              ofproto_unixctl_fdb_flush, NULL);
     unixctl_command_register("fdb/show", "bridge", 1, 1,
diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
index 24bbd884c..95b8dd7ce 100644
--- a/tests/ofproto-dpif.at
+++ b/tests/ofproto-dpif.at
@@ -6753,6 +6753,46 @@ PORTNAME
         portName=p2
 ])])
 
+AT_SETUP([ofproto-dpif - static MAC programming])
+OVS_VSWITCHD_START([set bridge br0 fail-mode=standalone])
+add_of_ports br0 1 2
+
+arp='eth_type(0x0806),arp(sip=192.168.0.1,tip=192.168.0.2,op=1,sha=50:54:00:00:00:05,tha=00:00:00:00:00:00)'
+
+AT_CHECK([ovs-appctl fdb/add br0 p1 0 50:54:00:00:00:05], [0], [OK
+])
+
+dnl Check for the static MAC entry is programmed
+AT_CHECK([ovs-appctl fdb/show br0], [0], [dnl
+ port  VLAN  MAC                Age
+    1     0  50:54:00:00:00:05   -1
+])
+
+dnl Trace an ARP packet arriving on p1
+OFPROTO_TRACE(
+  [ovs-dummy],
+  [in_port(1),eth(src=50:54:00:00:00:05,dst=ff:ff:ff:ff:ff:ff),$arp],
+  [-generate],
+  [2,100])
+
+dnl Check that age on static MAC entry is not altered because of above learning event
+AT_CHECK([ovs-appctl fdb/show br0], [0], [dnl
+ port  VLAN  MAC                Age
+    1     0  50:54:00:00:00:05   -1
+])
+
+dnl Remove static mac entry
+AT_CHECK([ovs-appctl fdb/del br0 p1 0 50:54:00:00:00:05], [0], [OK
+])
+
+dnl Check that entry is removed
+AT_CHECK([ovs-appctl fdb/show br0], [0], [dnl
+ port  VLAN  MAC                Age
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
 AT_SETUP([ofproto-dpif - basic truncate action])
 OVS_VSWITCHD_START
 add_of_ports br0 1 2 3 4 5
