/* vim: set ts=2 et sw=2 : */
/** @file udp.c */
/*
 *  T50 - Experimental Mixed Packet Injector
 *
 *  Copyright (C) 2010 - 2025 - T50 developers
 *
 *  This program 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 2 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 this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <stddef.h>
#include <assert.h>
#include <linux/udp.h>

#include <t50_config.h>
#include <t50_cksum.h>
#include <t50_memalloc.h>
#include <t50_modules.h>
#include <t50_randomizer.h>

/**
 * UDP packet header configuration.
 *
 * Configures the UDP packet header.
 * A pointer to this function will be on modules table.
 *
 * @param co Pointer to T50 configuration structure.
 * @param size Pointer to packet size (updated by the function).
 */
void udp(const config_options_T *const restrict co, unsigned int *restrict size)
{
  unsigned int length;

  struct iphdr *ip;
  struct iphdr *gre_ip;
  struct udphdr *udp;
  struct psdhdr *pseudo;

  assert(co != NULL);

  length = gre_opt_len(co);
  *size = sizeof(struct iphdr)  +
          sizeof(struct udphdr) +
          sizeof(struct psdhdr) +
          length;

  /* Try to reallocate packet, if necessary */
  alloc_packet(*size);

  /* Fill IP header. */
  ip = ip_header(packet, *size, co);

  gre_ip = gre_encapsulation(packet, co,
                             sizeof(struct iphdr) +
                             sizeof(struct udphdr));

  // Pointers to calculate the checksum.
  char *begin_, *end_;

  begin_ = (char *)(ip + 1) + length;

  /* UDP Header structure making a pointer to  IP Header structure. */
  udp = (struct udphdr *) begin_;

  *udp = (struct udphdr) {
    .source = IPPORT_RND(co->source),
    .dest = IPPORT_RND(co->dest),
    .len = htons(sizeof * udp)
  };

  /* Fill PSEUDO Header structure. */
  pseudo  = (struct psdhdr *) ( udp + 1 );

  *pseudo = (struct psdhdr) {
    .protocol = co->ip.protocol,
    .len = htons(sizeof * udp)
  };

  if (co->encapsulated)
  {
    pseudo->saddr = gre_ip->saddr;
    pseudo->daddr = gre_ip->daddr;
  }
  else
  {
    pseudo->saddr = ip->saddr;
    pseudo->daddr = ip->daddr;
  }

  end_ = (char *)(pseudo + 1);

  /* Computing the checksum. */
  udp->check  = co->bogus_csum ? RANDOM() :
                htons(cksum(begin_, end_ - begin_));

  gre_checksum(packet, co, *size);
}
