]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
OSPF: Support of authentication trailer for OSPFv3
authorOndrej Zajicek (work) <santiago@crfreenet.org>
Wed, 25 Apr 2018 13:50:57 +0000 (15:50 +0200)
committerOndrej Zajicek (work) <santiago@crfreenet.org>
Wed, 25 Apr 2018 13:54:53 +0000 (15:54 +0200)
Implement RFC 7166, crypthographic authentication for OSPFv3
analogous to authentication used for OSPFv2.

proto/ospf/config.Y
proto/ospf/dbdes.c
proto/ospf/hello.c
proto/ospf/iface.c
proto/ospf/neighbor.c
proto/ospf/ospf.h
proto/ospf/packet.c

index 005f43817ed0e7cf3aeaffb1a0e292b3d0ebf6b7..0b09966db5afeb9e2774496216ec4e0ccbe80c93 100644 (file)
@@ -37,7 +37,7 @@ ospf_iface_finish(void)
 
   ip->passwords = get_passwords();
 
-  if ((ip->autype == OSPF_AUTH_CRYPT) && (ip->helloint < 5))
+  if (ospf_cfg_is_v2() && (ip->autype == OSPF_AUTH_CRYPT) && (ip->helloint < 5))
     log(L_WARN "Hello or poll interval less that 5 makes cryptographic authenication prone to replay attacks");
 
   if ((ip->autype == OSPF_AUTH_NONE) && (ip->passwords != NULL))
@@ -54,6 +54,9 @@ ospf_iface_finish(void)
       /* Set default OSPF crypto algorithms */
       if (!pass->alg && (ip->autype == OSPF_AUTH_CRYPT))
        pass->alg = ospf_cfg_is_v2() ? ALG_MD5 : ALG_HMAC_SHA256;
+
+      if (ospf_cfg_is_v3() && ip->autype && (pass->alg < ALG_HMAC))
+       cf_error("Keyed hash algorithms are not allowed, use HMAC algorithms");
     }
   }
 }
@@ -181,7 +184,7 @@ static inline void
 ospf_check_auth(void)
 {
   if (ospf_cfg_is_v3())
-    cf_error("Authentication not supported in OSPFv3");
+    cf_error("Plaintext authentication not supported in OSPFv3");
 }
 
 
@@ -346,8 +349,8 @@ ospf_vlink_item:
  | DEAD COUNT expr { OSPF_PATT->deadc = $3 ; if ($3<=1) cf_error("Dead count must be greater than one"); }
  | AUTHENTICATION NONE { OSPF_PATT->autype = OSPF_AUTH_NONE;  }
  | AUTHENTICATION SIMPLE { OSPF_PATT->autype = OSPF_AUTH_SIMPLE; ospf_check_auth(); }
- | AUTHENTICATION CRYPTOGRAPHIC { OSPF_PATT->autype = OSPF_AUTH_CRYPT; ospf_check_auth(); }
- | password_list { ospf_check_auth(); }
+ | AUTHENTICATION CRYPTOGRAPHIC { OSPF_PATT->autype = OSPF_AUTH_CRYPT; }
+ | password_list
  ;
 
 ospf_vlink_start: VIRTUAL LINK idval
@@ -396,7 +399,7 @@ ospf_iface_item:
  | NEIGHBORS '{' nbma_list '}'
  | AUTHENTICATION NONE { OSPF_PATT->autype = OSPF_AUTH_NONE; }
  | AUTHENTICATION SIMPLE { OSPF_PATT->autype = OSPF_AUTH_SIMPLE; ospf_check_auth(); }
- | AUTHENTICATION CRYPTOGRAPHIC { OSPF_PATT->autype = OSPF_AUTH_CRYPT; ospf_check_auth(); }
+ | AUTHENTICATION CRYPTOGRAPHIC { OSPF_PATT->autype = OSPF_AUTH_CRYPT; }
  | RX BUFFER NORMAL { OSPF_PATT->rx_buffer = 0; }
  | RX BUFFER LARGE { OSPF_PATT->rx_buffer = OSPF_MAX_PKT_SIZE; }
  | RX BUFFER expr { OSPF_PATT->rx_buffer = $3; if (($3 < OSPF_MIN_PKT_SIZE) || ($3 > OSPF_MAX_PKT_SIZE)) cf_error("Buffer size must be in range 256-65535"); }
@@ -406,7 +409,7 @@ ospf_iface_item:
  | TTL SECURITY bool { OSPF_PATT->ttl_security = $3; }
  | TTL SECURITY TX ONLY { OSPF_PATT->ttl_security = 2; }
  | BFD bool { OSPF_PATT->bfd = $2; cf_check_bfd($2); }
- | password_list { ospf_check_auth(); }
+ | password_list
  ;
 
 pref_list:
index f211935f36974d3057afee46e8c484b29d06b58f..2626a24c34a4c20e518262d1b3a1557f9af7332b 100644 (file)
@@ -14,7 +14,7 @@
 struct ospf_dbdes2_packet
 {
   struct ospf_packet hdr;
-  union ospf_auth auth;
+  union ospf_auth2 auth;
 
   u16 iface_mtu;
   u8 options;
@@ -38,6 +38,13 @@ struct ospf_dbdes3_packet
 };
 
 
+uint
+ospf_dbdes3_options(struct ospf_packet *pkt)
+{
+  struct ospf_dbdes3_packet *ps = (void *) pkt;
+  return ntohl(ps->options);
+}
+
 static inline uint
 ospf_dbdes_hdrlen(struct ospf_proto *p)
 {
@@ -45,7 +52,6 @@ ospf_dbdes_hdrlen(struct ospf_proto *p)
     sizeof(struct ospf_dbdes2_packet) : sizeof(struct ospf_dbdes3_packet);
 }
 
-
 static void
 ospf_dbdes_body(struct ospf_proto *p, struct ospf_packet *pkt,
                struct ospf_lsa_header **body, uint *count)
@@ -129,7 +135,7 @@ ospf_prepare_dbdes(struct ospf_proto *p, struct ospf_neighbor *n)
   else /* OSPFv3 */
   {
     struct ospf_dbdes3_packet *ps = (void *) pkt;
-    ps->options = htonl(ifa->oa->options);
+    ps->options = htonl(ifa->oa->options | (ifa->autype == OSPF_AUTH_CRYPT ? OPT_AT : 0));
     ps->iface_mtu = htons(iface_mtu);
     ps->padding = 0;
     ps->imms = 0;      /* Will be set later */
index e706ea0f63cfdf8dec6c98d1688779d06125346d..523c24addb08bbc6ede34a2c510023ee163df485 100644 (file)
@@ -14,7 +14,7 @@
 struct ospf_hello2_packet
 {
   struct ospf_packet hdr;
-  union ospf_auth auth;
+  union ospf_auth2 auth;
 
   u32 netmask;
   u16 helloint;
@@ -42,6 +42,13 @@ struct ospf_hello3_packet
 };
 
 
+uint
+ospf_hello3_options(struct ospf_packet *pkt)
+{
+  struct ospf_hello3_packet *ps = (void *) pkt;
+  return ntohl(ps->options) & 0x00FFFFFF;
+}
+
 void
 ospf_send_hello(struct ospf_iface *ifa, int kind, struct ospf_neighbor *dirn)
 {
@@ -86,9 +93,10 @@ ospf_send_hello(struct ospf_iface *ifa, int kind, struct ospf_neighbor *dirn)
   else
   {
     struct ospf_hello3_packet *ps = (void *) pkt;
+    u32 options = ifa->oa->options | (ifa->autype == OSPF_AUTH_CRYPT ? OPT_AT : 0);
 
     ps->iface_id = htonl(ifa->iface_id);
-    ps->options = ntohl(ifa->oa->options | (ifa->priority << 24));
+    ps->options = ntohl(options | (ifa->priority << 24));
     ps->helloint = ntohs(ifa->helloint);
     ps->deadint = htons(ifa->deadint);
     ps->dr = htonl(ifa->drid);
@@ -334,7 +342,6 @@ ospf_receive_hello(struct ospf_packet *pkt, struct ospf_iface *ifa,
   n->priority = rcv_priority;
   n->iface_id = rcv_iface_id;
 
-
   /* Update inactivity timer */
   ospf_neigh_sm(n, INM_HELLOREC);
 
index e3d8d61bdb0ce9f3770fd496140ec089c0df4248..19bcfa15b014608d45626c1e641e76d1c461d115 100644 (file)
@@ -61,7 +61,10 @@ ifa_tx_hdrlen(struct ospf_iface *ifa)
 
   /* Relevant just for OSPFv2 */
   if (ifa->autype == OSPF_AUTH_CRYPT)
+  {
+    hlen += ospf_is_v2(p) ? 0 : sizeof(struct ospf_auth3);
     hlen += max_mac_length(ifa->passwords);
+  }
 
   return hlen;
 }
@@ -137,7 +140,7 @@ ospf_sk_open(struct ospf_iface *ifa)
     goto err;
 
   /* 12 is an offset of the checksum in an OSPFv3 packet */
-  if (ospf_is_v3(p))
+  if (ospf_is_v3(p) && !ifa->autype)
     if (sk_set_ipv6_checksum(sk, 12) < 0)
       goto err;
 
@@ -828,6 +831,14 @@ ospf_iface_reconfigure(struct ospf_iface *ifa, struct ospf_iface_patt *new)
   {
     OSPF_TRACE(D_EVENTS, "Changing authentication type of %s", ifname);
     ifa->autype = new->autype;
+
+    /* For OSPFv3, we need to update checksum calculation by OS */
+    if (ospf_is_v3(p) && ifa->sk)
+      if (sk_set_ipv6_checksum(ifa->sk, ifa->autype ? -1 : 12) < 0)
+      {
+       sk_log_error(ifa->sk, p->p.name);
+       return 0;
+      }
   }
 
   /* Update passwords */
index f2d3505ecc69ff6fb703a37866235c35a2573c8e..7ce682b0ab2a7ddcd155d0459b38eac36d89315d 100644 (file)
@@ -84,8 +84,6 @@ ospf_neighbor_new(struct ospf_iface *ifa)
   n->pool = pool;
   n->ifa = ifa;
   add_tail(&ifa->neigh_list, NODE n);
-  n->adj = 0;
-  n->csn = 0;
   n->state = NEIGHBOR_DOWN;
 
   init_lists(p, n);
index 54eeb74c112a41fb3839367deae926a74d102696..f26ed99ccb4a98aa32938691a492437c6c98b6d2 100644 (file)
@@ -69,6 +69,9 @@
 #define MINLSARRIVAL           (1 S_)
 #define LSINFINITY             0xffffff
 
+#define OSPF_PKT_TYPES         5       /* HELLO_P .. LSACK_P */
+#define OSPF3_CRYPTO_ID                1       /* OSPFv3 Cryptographic Protocol ID */
+
 #define OSPF_DEFAULT_TICK 1
 #define OSPF_DEFAULT_STUB_COST 1000
 #define OSPF_DEFAULT_ECMP_LIMIT 16
@@ -222,6 +225,7 @@ struct ospf_proto
   u8 merge_external;           /* Should i merge external routes? */
   u8 asbr;                     /* May i originate any ext/NSSA lsa? */
   u8 ecmp;                     /* Maximal number of nexthops in ECMP route, or 0 */
+  u64 csn64;                   /* Last used cryptographic sequence number */
   struct ospf_area *backbone;  /* If exists */
   event *flood_event;          /* Event for flooding LS updates */
   void *lsab;                  /* LSA buffer used when originating router LSAs */
@@ -254,8 +258,6 @@ struct ospf_area
   struct fib rtr;              /* Routing tables for routers */
 };
 
-
-
 struct ospf_iface
 {
   node n;
@@ -387,7 +389,8 @@ struct ospf_neighbor
   struct bfd_request *bfd_req; /* BFD request, if BFD is used */
   void *ldd_buffer;            /* Last database description packet */
   u32 ldd_bsize;               /* Buffer size for ldd_buffer */
-  u32 csn;                      /* Last received crypt seq number (for MD5) */
+  u32 csn;                     /* OSPFv2: Last received crypt seq number */
+  u64 csn64[OSPF_PKT_TYPES];   /* OSPFv3: Last received CSN for each type of packet */
 };
 
 
@@ -422,6 +425,7 @@ struct ospf_neighbor
 #define OSPF_AUTH_SIMPLE 1
 #define OSPF_AUTH_CRYPT        2
 
+#define OSPF3_AUTH_HMAC 1      /* HMAC Cryptographic Authentication */
 
 /* OSPF neighbor states */
 #define NEIGHBOR_DOWN  0
@@ -459,10 +463,12 @@ struct ospf_neighbor
 #define OPT_MC         0x0004  /* Related to MOSPF, not used and obsolete */
 #define OPT_N          0x0008  /* Related to NSSA */
 #define OPT_P          0x0008  /* OSPFv2, flags P and N share position, see NSSA RFC */
-#define OPT_EA         0x0010  /* OSPFv2, external attributes, not used and obsolete */
+#define OPT_L_V2       0x0010  /* OSPFv2, link-local signaling, not used */
 #define OPT_R          0x0010  /* OSPFv3, originator is active router */
 #define OPT_DC         0x0020  /* Related to demand circuits, not used */
 #define OPT_AF         0x0100  /* OSPFv3 Address Families (RFC 5838) */
+#define OPT_L_V3       0x0200  /* OSPFv3, link-local signaling */
+#define OPT_AT          0x0400 /* OSPFv3, authentication trailer */
 
 /* Router-LSA VEB flags are are stored together with links (OSPFv2) or options (OSPFv3) */
 #define OPT_RT_B       (0x01 << 24)
@@ -489,20 +495,38 @@ struct ospf_packet
   u8 autype;                   /* Undefined for OSPFv3 */
 };
 
+struct ospf_lls
+{
+  u16 checksum;
+  u16 length;
+  byte data[0];
+};
+
 struct ospf_auth_crypto
 {
   u16 zero;
   u8 keyid;
   u8 len;
-  u32 csn;
+  u32 csn;                     /* Cryptographic sequence number (32-bit) */
 };
 
-union ospf_auth
+union ospf_auth2
 {
   u8 password[8];
   struct ospf_auth_crypto c32;
 };
 
+struct ospf_auth3
+{
+  u16 type;                    /* Authentication type (OSPF3_AUTH_*) */
+  u16 length;                  /* Authentication trailer length (header + data) */
+  u16 reserved;
+  u16 sa_id;                   /* Security association identifier (key_id) */
+  u64 csn;                     /* Cryptographic sequence number (64-bit) */
+  byte data[0];                        /* Authentication data */
+};
+
+
 /* Packet types */
 #define HELLO_P                1       /* Hello */
 #define DBDES_P                2       /* Database description */
@@ -957,7 +981,7 @@ static inline void ospf_send_to_des(struct ospf_iface *ifa)
 #endif
 
 static inline uint ospf_pkt_hdrlen(struct ospf_proto *p)
-{ return ospf_is_v2(p) ? (sizeof(struct ospf_packet) + sizeof(union ospf_auth)) : sizeof(struct ospf_packet); }
+{ return ospf_is_v2(p) ? (sizeof(struct ospf_packet) + sizeof(union ospf_auth2)) : sizeof(struct ospf_packet); }
 
 static inline void * ospf_tx_buffer(struct ospf_iface *ifa)
 { return ifa->sk->tbuf; }
@@ -969,11 +993,13 @@ static inline void * ospf_tx_buffer(struct ospf_iface *ifa)
 
 void ospf_send_hello(struct ospf_iface *ifa, int kind, struct ospf_neighbor *dirn);
 void ospf_receive_hello(struct ospf_packet *pkt, struct ospf_iface *ifa, struct ospf_neighbor *n, ip_addr faddr);
+uint ospf_hello3_options(struct ospf_packet *pkt);
 
 /* dbdes.c */
 void ospf_send_dbdes(struct ospf_proto *p, struct ospf_neighbor *n);
 void ospf_rxmt_dbdes(struct ospf_proto *p, struct ospf_neighbor *n);
 void ospf_receive_dbdes(struct ospf_packet *pkt, struct ospf_iface *ifa, struct ospf_neighbor *n);
+uint ospf_dbdes3_options(struct ospf_packet *pkt);
 
 /* lsreq.c */
 void ospf_send_lsreq(struct ospf_proto *p, struct ospf_neighbor *n);
index 38d7a75face5181bda39b2c1a63537ed60ca6733..db387661c90fc0b8cf4fb5056c57b72b5bba735e 100644 (file)
@@ -29,23 +29,24 @@ ospf_pkt_fill_hdr(struct ospf_iface *ifa, void *buf, u8 h_type)
   pkt->areaid = htonl(ifa->oa->areaid);
   pkt->checksum = 0;
   pkt->instance_id = ifa->instance_id;
-  pkt->autype = ifa->autype;
+  pkt->autype = ospf_is_v2(p) ? ifa->autype : 0;
 }
 
 /* We assume OSPFv2 in ospf_pkt_finalize() */
 static void
-ospf_pkt_finalize(struct ospf_iface *ifa, struct ospf_packet *pkt, uint *plen)
+ospf_pkt_finalize2(struct ospf_iface *ifa, struct ospf_packet *pkt, uint *plen)
 {
+  struct ospf_proto *p = ifa->oa->po;
   struct password_item *pass = NULL;
-  union ospf_auth *auth = (void *) (pkt + 1);
-
-  pkt->checksum = 0;
-  pkt->autype = ifa->autype;
-  bzero(auth, sizeof(union ospf_auth));
+  union ospf_auth2 *auth = (void *) (pkt + 1);
+  memset(auth, 0, sizeof(union ospf_auth2));
 
   /* Compatibility note: auth may contain anything if autype is
      none, but nonzero values do not work with Mikrotik OSPF */
 
+  pkt->checksum = 0;
+  pkt->autype = ifa->autype;
+
   switch (ifa->autype)
   {
   case OSPF_AUTH_SIMPLE:
@@ -60,7 +61,7 @@ ospf_pkt_finalize(struct ospf_iface *ifa, struct ospf_packet *pkt, uint *plen)
   case OSPF_AUTH_NONE:
     {
       void *body = (void *) (auth + 1);
-      uint blen = *plen - sizeof(struct ospf_packet) - sizeof(union ospf_auth);
+      uint blen = *plen - sizeof(struct ospf_packet) - sizeof(union ospf_auth2);
       pkt->checksum = ipsum_calculate(pkt, sizeof(struct ospf_packet), body, blen, NULL);
     }
     break;
@@ -69,7 +70,7 @@ ospf_pkt_finalize(struct ospf_iface *ifa, struct ospf_packet *pkt, uint *plen)
     pass = password_find(ifa->passwords, 0);
     if (!pass)
     {
-      log(L_ERR "No suitable password found for authentication");
+      log(L_ERR "%s: No suitable password found for authentication", p->p.name);
       return;
     }
 
@@ -105,8 +106,7 @@ ospf_pkt_finalize(struct ospf_iface *ifa, struct ospf_packet *pkt, uint *plen)
     else
       memset32(auth_tail, HMAC_MAGIC, auth_len / 4);
 
-    mac_fill(pass->alg, pass->password, pass->length,
-            (byte *) pkt, *plen, auth_tail);
+    mac_fill(pass->alg, pass->password, pass->length, (byte *) pkt, *plen, auth_tail);
     break;
 
   default:
@@ -114,13 +114,63 @@ ospf_pkt_finalize(struct ospf_iface *ifa, struct ospf_packet *pkt, uint *plen)
   }
 }
 
+/*
+ * Return an extra packet size that should be added to a final packet size
+ */
+static void
+ospf_pkt_finalize3(struct ospf_iface *ifa, struct ospf_packet *pkt, uint *plen, ip_addr src)
+{
+  struct ospf_proto *p = ifa->oa->po;
+  struct ospf_auth3 *auth = (void *) ((byte *) pkt + *plen);
+
+  pkt->checksum = 0;
+  pkt->autype = 0;
+
+  if (ifa->autype != OSPF_AUTH_CRYPT)
+    return;
+
+  struct password_item *pass = password_find(ifa->passwords, 0);
+  if (!pass)
+  {
+    log(L_ERR "%s: No suitable password found for authentication", p->p.name);
+    return;
+  }
+
+  /* FIXME: Ensure persistence */
+  p->csn64++;
+
+  uint mac_len = mac_type_length(pass->alg);
+  uint auth_len = sizeof(struct ospf_auth3) + mac_len;
+  *plen += auth_len;
+
+  ASSERT(*plen < ifa->sk->tbsize);
+
+  memset(auth, 0, sizeof(struct ospf_auth3));
+  auth->type = htons(OSPF3_AUTH_HMAC);
+  auth->length = htons(auth_len);
+  auth->reserved = 0;
+  auth->sa_id = htons(pass->id);
+  put_u64(&auth->csn, p->csn64);
+
+  /* Initialize with src IP address padded with HMAC_MAGIC */
+  put_ip6(auth->data, ipa_to_ip6(src));
+  memset32(auth->data + 16, HMAC_MAGIC, (mac_len - 16) / 4);
+
+  /* Attach OSPFv3 Cryptographic Protocol ID to the key */
+  uint pass_len = pass->length + 2;
+  byte *pass_key = alloca(pass_len);
+  memcpy(pass_key, pass->password, pass->length);
+  put_u16(pass_key + pass->length, OSPF3_CRYPTO_ID);
+
+  mac_fill(pass->alg, pass_key, pass_len, (byte *) pkt, *plen, auth->data);
+}
+
 
-/* We assume OSPFv2 in ospf_pkt_checkauth() */
 static int
-ospf_pkt_checkauth(struct ospf_neighbor *n, struct ospf_iface *ifa, struct ospf_packet *pkt, uint len)
+ospf_pkt_checkauth2(struct ospf_neighbor *n, struct ospf_iface *ifa, struct ospf_packet *pkt, uint len)
 {
   struct ospf_proto *p = ifa->oa->po;
-  union ospf_auth *auth = (void *) (pkt + 1);
+  union ospf_auth2 *auth = (void *) (pkt + 1);
   struct password_item *pass = NULL;
   const char *err_dsc = NULL;
   uint err_val = 0;
@@ -200,6 +250,119 @@ drop:
   return 0;
 }
 
+static int
+ospf_pkt_checkauth3(struct ospf_neighbor *n, struct ospf_iface *ifa, struct ospf_packet *pkt, uint len, ip_addr src)
+{
+  struct ospf_proto *p = ifa->oa->po;
+  const char *err_dsc = NULL;
+  uint err_val = 0;
+
+  uint plen = ntohs(pkt->length);
+  uint opts, lls_present, auth_present;
+
+  /*
+   * When autentication is not enabled, ignore the trailer. This is different
+   * from OSPFv2, but it is necessary in order to support migration modes. Note
+   * that regular authenticated packets do not have valid checksum and will be
+   * dropped by OS on non-authenticated ifaces.
+   */
+  if (ifa->autype != OSPF_AUTH_CRYPT)
+    return 1;
+
+  switch(pkt->type)
+  {
+  case HELLO_P:
+    opts = ospf_hello3_options(pkt);
+    lls_present = opts & OPT_L_V3;
+    auth_present = opts & OPT_AT;
+    break;
+
+  case DBDES_P:
+    opts = ospf_dbdes3_options(pkt);
+    lls_present = opts & OPT_L_V3;
+    auth_present = opts & OPT_AT;
+    break;
+
+  default:
+    lls_present = 0;
+    auth_present = n->options & OPT_AT;
+  }
+
+  if (!auth_present)
+    DROP1("missing authentication trailer");
+
+  if (lls_present)
+  {
+    if ((plen + sizeof(struct ospf_lls)) > len)
+      DROP("packet length mismatch", len);
+
+    struct ospf_lls *lls = (void *) ((byte *) pkt + plen);
+    plen += ntohs(lls->length);
+  }
+
+  if ((plen + sizeof(struct ospf_auth3)) > len)
+    DROP("packet length mismatch", len);
+
+  struct ospf_auth3 *auth = (void *) ((byte *) pkt + plen);
+
+  uint rcv_auth_type = ntohs(auth->type);
+  if (rcv_auth_type != OSPF3_AUTH_HMAC)
+    DROP("authentication method mismatch", rcv_auth_type);
+
+  uint rcv_auth_len = ntohs(auth->length);
+  if (plen + rcv_auth_len > len)
+    DROP("packet length mismatch", len);
+
+  uint rcv_key_id = ntohs(auth->sa_id);
+  struct password_item *pass = password_find_by_id(ifa->passwords, rcv_key_id);
+  if (!pass)
+    DROP("no suitable password found", rcv_key_id);
+
+  uint mac_len = mac_type_length(pass->alg);
+  if (rcv_auth_len != (sizeof(struct ospf_auth3) + mac_len))
+    DROP("wrong authentication length", rcv_auth_len);
+
+  uint pt = pkt->type - 1;
+  u64 rcv_csn = get_u64(&auth->csn);
+  if (n && (rcv_csn <= n->csn64[pt]))
+  {
+    /* We want to report both new and old CSN */
+    LOG_PKT_AUTH("Authentication failed for nbr %R on %s - "
+                "lower sequence number (rcv %u, old %u)",
+                n->rid, ifa->ifname, (uint) rcv_csn, (uint) n->csn64[pt]);
+    return 0;
+  }
+
+  /* Save the received authentication data */
+  byte *auth_data = alloca(mac_len);
+  memcpy(auth_data, auth->data, mac_len);
+
+  /* Initialize with src IP address padded with HMAC_MAGIC */
+  put_ip6(auth->data, ipa_to_ip6(src));
+  memset32(auth->data + 16, HMAC_MAGIC, (mac_len - 16) / 4);
+
+  /* Attach OSPFv3 Cryptographic Protocol ID to the key */
+  uint pass_len = pass->length + 2;
+  byte *pass_key = alloca(pass_len);
+  memcpy(pass_key, pass->password, pass->length);
+  put_u16(pass_key + pass->length, OSPF3_CRYPTO_ID);
+
+  if (!mac_verify(pass->alg, pass_key, pass_len,
+                 (byte *) pkt, plen + rcv_auth_len, auth_data))
+    DROP("wrong authentication code", pass->id);
+
+  if (n)
+    n->csn64[pt] = rcv_csn;
+
+  return 1;
+
+drop:
+  LOG_PKT_AUTH("Authentication failed for nbr %R on %s - %s (%u)",
+              (n ? n->rid : ntohl(pkt->routerid)), ifa->ifname, err_dsc, err_val);
+
+  return 0;
+}
+
 /**
  * ospf_rx_hook
  * @sk: socket we received the packet.
@@ -298,12 +461,12 @@ ospf_rx_hook(sock *sk, uint len)
 
   if (ospf_is_v2(p) && (pkt->autype != OSPF_AUTH_CRYPT))
   {
-    uint hlen = sizeof(struct ospf_packet) + sizeof(union ospf_auth);
+    uint hlen = sizeof(struct ospf_packet) + sizeof(union ospf_auth2);
     uint blen = plen - hlen;
     void *body = ((void *) pkt) + hlen;
 
     if (!ipsum_verify(pkt, sizeof(struct ospf_packet), body, blen, NULL))
-      DROP1("invalid checksum");
+      DROP("invalid checksum", ntohs(pkt->checksum));
   }
 
   /* Third, we resolve associated iface and handle vlinks. */
@@ -401,8 +564,14 @@ found:
     return 1;
   }
 
+  /* Check packet type here, ospf_pkt_checkauth3() expects valid values */
+  if (pkt->type < HELLO_P || pkt->type > LSACK_P)
+    DROP("invalid packet type", pkt->type);
+
   /* ospf_pkt_checkauth() has its own error logging */
-  if (ospf_is_v2(p) && !ospf_pkt_checkauth(n, ifa, pkt, len))
+  if ((ospf_is_v2(p) ?
+       !ospf_pkt_checkauth2(n, ifa, pkt, len) :
+       !ospf_pkt_checkauth3(n, ifa, pkt, len, sk->faddr)))
     return 1;
 
   switch (pkt->type)
@@ -426,9 +595,6 @@ found:
   case LSACK_P:
     ospf_receive_lsack(pkt, ifa, n);
     break;
-
-  default:
-    DROP("invalid packet type", pkt->type);
   };
   return 1;
 
@@ -452,7 +618,7 @@ ospf_tx_hook(sock * sk)
 void
 ospf_err_hook(sock * sk, int err)
 {
-  struct ospf_iface *ifa= (struct ospf_iface *) (sk->data);
+  struct ospf_iface *ifa = (struct ospf_iface *) (sk->data);
   struct ospf_proto *p = ifa->oa->po;
   log(L_ERR "%s: Socket error on %s: %M", p->p.name, ifa->ifname, err);
 }
@@ -472,7 +638,9 @@ ospf_send_to(struct ospf_iface *ifa, ip_addr dst)
   uint plen = ntohs(pkt->length);
 
   if (ospf_is_v2(ifa->oa->po))
-    ospf_pkt_finalize(ifa, pkt, &plen);
+    ospf_pkt_finalize2(ifa, pkt, &plen);
+  else
+    ospf_pkt_finalize3(ifa, pkt, &plen, sk->saddr);
 
   int done = sk_send_to(sk, plen, dst, 0);
   if (!done)