diff -c -r 2.1.2/src/core/ipv6/icmp6.c 2.1.2withRA/src/core/ipv6/icmp6.c
*** 2.1.2/src/core/ipv6/icmp6.c	2018-02-20 18:16:31.000000000 +0800
--- 2.1.2withRA/src/core/ipv6/icmp6.c	2019-01-27 15:57:12.936159200 +0800
***************
*** 123,128 ****
--- 123,131 ----
    case ICMP6_TYPE_RS:
  #if LWIP_IPV6_FORWARD
      /* @todo implement router functionality */
+   #if LWIP_IPV6_SEND_ROUTER_ADVERTISE
+     nd6_input(p, inp);
+   #endif
  #endif
      break;
  #if LWIP_IPV6_MLD
diff -c -r 2.1.2/src/core/ipv6/nd6.c 2.1.2withRA/src/core/ipv6/nd6.c
*** 2.1.2/src/core/ipv6/nd6.c	2018-07-11 03:52:28.000000000 +0800
--- 2.1.2withRA/src/core/ipv6/nd6.c	2019-01-27 19:50:12.419878800 +0800
***************
*** 128,133 ****
--- 128,137 ----
  static err_t nd6_send_rs(struct netif *netif);
  #endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */
  
+ #if LWIP_IPV6_SEND_ROUTER_ADVERTISE
+ static err_t nd6_send_ra(struct netif *netif, ip6_addr_t *target_addr);
+ #endif /* LWIP_IPV6_SEND_ROUTER_ADVERTISE */
+ 
  #if LWIP_ND6_QUEUEING
  static void nd6_free_q(struct nd6_q_entry *q);
  #else /* LWIP_ND6_QUEUEING */
***************
*** 551,556 ****
--- 555,584 ----
  
      break; /* ICMP6_TYPE_NS */
    }
+ #if LWIP_IPV6_SEND_ROUTER_ADVERTISE
+   case ICMP6_TYPE_RS: /* Router Solicitation. */
+   {
+     /* Check that RS header fits in packet. */
+     if (p->len < sizeof(struct rs_header)) {
+       /* @todo debug message */
+       pbuf_free(p);
+       ND6_STATS_INC(nd6.lenerr);
+       ND6_STATS_INC(nd6.drop);
+       return;
+     }
+ 
+     /* Just skip the link layer address option for simple. */
+ 	if (!ip6_addr_isany(ip6_current_src_addr())) {
+ 	  nd6_send_ra(inp, ip6_current_src_addr());
+ 	} else {
+ 	  nd6_send_ra(inp, NULL);
+ 	}
+ 	LWIP_DEBUGF(ND6_DEBUG, ("nd6(%c%c): send solicited ra, target addr %s\n", 
+ 		inp->name[0], inp->name[1], ip6addr_ntoa(ip6_current_src_addr())));
+ 
+     break;
+   }
+ #endif /* LWIP_IPV6_SEND_ROUTER_ADVERTISE */
    case ICMP6_TYPE_RA: /* Router Advertisement. */
    {
      struct ra_header *ra_hdr;
***************
*** 1157,1162 ****
--- 1185,1230 ----
    }
  #endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */
  
+ #if LWIP_IPV6_SEND_ROUTER_ADVERTISE
+   /*   When a netif is up, ra will be sent LWIP_ND6_MAX_INITIAL_RA times 
+            after every LWIP_ND6_INITIAL_RA_INTERVAL seconds,
+ 	   then, each ra will be sent after every LWIP_ND6_NORMAL_RA_INTERVAL seconds.
+ 	   The procedure of ra sending is in nd6_tmr which is called every 1 seconds.
+ 	   RFC4861 suggests random ra interval to reduce the probability 
+ 	   of synchronization with ra from other routers on the same link.
+ 	   In our case, there is only one router on one link, so random is unnecesary.
+    */
+   for (netif = netif_list; netif != NULL; netif = netif->next) {
+     if ((netif->flags & NETIF_FLAG_UP) && (!ip6_addr_isinvalid(netif_ip6_addr_state(netif, 0)))) {
+       if (netif->ra_is_initial) {
+         if (netif->ra_initial_count > 0) {
+ 		  if (netif->ra_timer > 0) {
+ 		    netif->ra_timer--;
+ 		  } else {	/* Initial timer is expired, send ra, decrease the inital count, reset the timer */
+             nd6_send_ra(netif, NULL);
+             netif->ra_initial_count--;
+ 			netif->ra_timer = LWIP_ND6_INITIAL_RA_INTERVAL;
+ 			LWIP_DEBUGF(ND6_DEBUG, ("nd6(%c%c): send inital ra, left %d, interval %d\n", 
+ 				netif->name[0], netif->name[1], netif->ra_initial_count, netif->ra_timer));
+ 		  }
+ 		} else { 	/* Initial ra have been sent, set is_inital to false and reset the timer to normal ra interval */
+           netif->ra_is_initial = 0;
+ 		  netif->ra_timer = LWIP_ND6_NORMAL_RA_INTERVAL;
+ 		}
+       } else {
+         if (netif->ra_timer > 0) {
+ 		    netif->ra_timer--;
+ 		} else {	/* Normal ra timer is expired, send ra, reset the timer */
+           nd6_send_ra(netif, NULL);
+ 		  netif->ra_timer = LWIP_ND6_NORMAL_RA_INTERVAL;
+ 		  LWIP_DEBUGF(ND6_DEBUG, ("nd6(%c%c): send normal ra, interval %d\n", 
+ 		  	netif->name[0], netif->name[1], netif->ra_timer));
+ 		}
+       }
+     }
+   }
+ #endif /* LWIP_IPV6_SEND_ROUTER_ADVERTISE */
+ 
  }
  
  /** Send a neighbor solicitation message for a specific neighbor cache entry
***************
*** 1390,1395 ****
--- 1458,1558 ----
  }
  #endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */
  
+ #if LWIP_IPV6_SEND_ROUTER_ADVERTISE
+ /**
+  * Send a router advertisement message
+  *
+  * @param netif the netif on which to send the message
+  * @param target_addr when ra is sent solicited this is the source address of rs
+  * while should be NULL when is sent periodically
+  */
+ static err_t
+ nd6_send_ra(struct netif *netif, ip6_addr_t *target_addr)
+ {
+   struct ra_header *ra_hdr;
+   struct prefix_option *prefix_opt;
+   struct pbuf *p;
+   const ip6_addr_t *src_addr;
+   err_t err;
+   u32_t ra_prefix[2];
+   u16_t prefix_opt_len;
+ 
+   /* Get prefix of the first global address which should be in slot 1 */
+   if (ip6_addr_isvalid(netif_ip6_addr_state(netif, 1))) {
+     ra_prefix[0] = netif_ip6_addr(netif, 1)->addr[0];
+ 	ra_prefix[1] = netif_ip6_addr(netif, 1)->addr[1];
+   } else {
+     return ERR_IF;
+   }
+ 
+   /* The source address must be link-local address */
+   if (ip6_addr_isvalid(netif_ip6_addr_state(netif, 0))) {
+     src_addr = netif_ip6_addr(netif, 0);
+   } else {
+     return ERR_IF;
+   }
+ 
+   /* The dest address should be the source of rs or the all routers target address. */
+   if (target_addr == NULL) {
+     ip6_addr_set_allnodes_linklocal(&multicast_address);
+   }
+ 
+   /* Allocate a packet, we only send prefix option. */
+   prefix_opt_len = sizeof(struct prefix_option);
+   p = pbuf_alloc(PBUF_IP, sizeof(struct ra_header) + prefix_opt_len, PBUF_RAM);
+   if (p == NULL) {
+     ND6_STATS_INC(nd6.memerr);
+     return ERR_BUF;
+   }
+ 
+   /* Set fields. */
+   ra_hdr = (struct ra_header *)p->payload;
+ 
+   ra_hdr->type = ICMP6_TYPE_RA;
+   ra_hdr->code = 0;
+   ra_hdr->chksum = 0;
+   ra_hdr->current_hop_limit = 0;	/* 0 means unspecified */
+   ra_hdr->flags = 0;				/* M and O flag, 0 means not managed by DHCPv6 or Other */
+   ra_hdr->router_lifetime = 0;		/* 0 means will not appear on the default router list */
+   ra_hdr->reachable_time = 0;		/* 0 means unspecified, used by nbr unreachability detection */
+   ra_hdr->retrans_timer = 0;  		/* 0 means unspecified, used by address resolution and nbr unreach detect */
+ 
+   prefix_opt = (struct prefix_option *)((u8_t *)p->payload + sizeof(struct ra_header));
+   
+   prefix_opt->type = ND6_OPTION_TYPE_PREFIX_INFO;
+   prefix_opt->length = 4;			/* in units of 8 byte, so 32 byte */
+   prefix_opt->prefix_length = 64;	/* prefix length */
+   prefix_opt->flags = ND6_PREFIX_FLAG_ON_LINK | ND6_PREFIX_FLAG_AUTONOMOUS;	/* on-link and autonomous */
+   prefix_opt->valid_lifetime = lwip_htonl(604800);
+   prefix_opt->preferred_lifetime = lwip_htonl(86400);
+   prefix_opt->reserved2[0] = 0;
+   prefix_opt->reserved2[1] = 0;
+   prefix_opt->reserved2[2] = 0;
+   prefix_opt->site_prefix_length = 0;			/* rfc4861 does not have this field */
+   prefix_opt->prefix.addr[0] = ra_prefix[0];
+   prefix_opt->prefix.addr[1] = ra_prefix[1];
+   prefix_opt->prefix.addr[2] = 0;
+   prefix_opt->prefix.addr[3] = 0;
+ 
+ #if CHECKSUM_GEN_ICMP6
+   IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_ICMP6) {
+     ra_hdr->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->len, src_addr,
+       (target_addr == NULL) ? &multicast_address : target_addr);
+   }
+ #endif /* CHECKSUM_GEN_ICMP6 */
+ 
+   /* Send the packet out. */
+   ND6_STATS_INC(nd6.xmit);
+ 
+   err = ip6_output_if(p, src_addr, (target_addr == NULL) ? &multicast_address : target_addr, 
+   	  LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, netif);
+   pbuf_free(p);
+ 
+   return err;
+ }
+ #endif /* LWIP_IPV6_SEND_ROUTER_ADVERTISE */
+ 
+ 
  /**
   * Search for a neighbor cache entry
   *
***************
*** 2429,2434 ****
--- 2592,2603 ----
    /* Send Router Solicitation messages (see RFC 4861, ch. 6.3.7). */
    netif->rs_count = LWIP_ND6_MAX_MULTICAST_SOLICIT;
  #endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */
+ 
+ #if LWIP_IPV6_SEND_ROUTER_ADVERTISE
+   netif->ra_is_initial = 1;
+   netif->ra_initial_count = LWIP_ND6_MAX_INITIAL_RA;
+   netif->ra_timer = LWIP_ND6_INITIAL_RA_INTERVAL;
+ #endif /* LWIP_IPV6_SEND_ROUTER_ADVERTISE */
  }
  
  #endif /* LWIP_IPV6 */
diff -c -r 2.1.2/src/include/lwip/netif.h 2.1.2withRA/src/include/lwip/netif.h
*** 2.1.2/src/include/lwip/netif.h	2018-08-19 02:21:24.000000000 +0800
--- 2.1.2withRA/src/include/lwip/netif.h	2019-01-27 15:45:21.111695300 +0800
***************
*** 356,361 ****
--- 356,369 ----
    /** Number of Router Solicitation messages that remain to be sent. */
    u8_t rs_count;
  #endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */
+ #if LWIP_IPV6_SEND_ROUTER_ADVERTISE
+   /* Flag to indicates whether we are in the initial phase of Router Advertisement sending */
+   u8_t ra_is_initial;
+   /** Number of Router Advertisement messages that remain to be sent in the initial phase. */
+   u8_t ra_initial_count;
+   /* Timer to decide when to send a Router Advertisement */
+   u32_t ra_timer;
+ #endif /* LWIP_IPV6_SEND_ROUTER_ADVERTISE */
  #if MIB2_STATS
    /** link type (from "snmp_ifType" enum from snmp_mib2.h) */
    u8_t link_type;
