/*
 * Sequence number / nonce mechanism for udp tunnel
 *
 * mechanisms: nonce, sequence
 * arguments: none
 *
 * restrictions: none
 * encoding: prepend 4 bytes of sequence arithmetic serial number
 * decoding: check increasingness (sequence), or ignore (nonce)
 */
/*
 * This file is part of ipif, part of userv-utils
 *
 * Copyright 1996-2013 Ian Jackson <ijackson@chiark.greenend.org.uk>
 * Copyright 1998 David Damerell <damerell@chiark.greenend.org.uk>
 * Copyright 1999,2003
 *    Chancellor Masters and Scholars of the University of Cambridge
 * Copyright 2010 Tony Finch <fanf@dotat.at>
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with userv-utils; if not, see http://www.gnu.org/licenses/.
 */

#include <netinet/in.h>

#include "forwarder.h"

struct mechdata {
  uint32_t number;
  int anyseen; /* decode only */
};

static void mes_sequence(struct mechdata **md_r, int *maxprefix_io, int *maxsuffix_io) {
  struct mechdata *md;

  XMALLOC(md);
  get_random(&md->number,sizeof(md->number));
  *maxprefix_io += 4;
  *md_r= md;
}

static void mds_sequence(struct mechdata **md_r) {
  struct mechdata *md;
  XMALLOC(md);
  md->anyseen= 0;
  *md_r= md;
}

static void menc_sequence(struct mechdata *md, struct buffer *buf) {
  md->number++;
  *(uint32_t*)buf_prepend(buf,4)= htonl(md->number);
}
  
static const char *mdec_check(struct mechdata *md, struct buffer *buf) {
  uint32_t *sp, sequence;

  BUF_UNPREPEND(sp,buf,4);
  sequence= ntohl(*sp);

  if (md->anyseen)
    if (sequence - md->number >= 0x800000UL) return "out of order packet";

  md->number= sequence;
  md->anyseen= 1;

  return 0;
}
  
static const char *mdec_skip(struct mechdata *md, struct buffer *buf) {
  uint32_t *sp;
  BUF_UNPREPEND(sp,buf,4);
  return 0;
}

const struct mechanism mechlist_sequence[]= {
 { "nonce",         mes_sequence, mds_sequence,  menc_sequence, mdec_skip    },
 { "sequence",      mes_sequence, mds_sequence,  menc_sequence, mdec_check   },
 { 0 }
};
