]> git.ipfire.org Git - thirdparty/FORT-validator.git/commitdiff
Read VRPs from a CSV file set at JSON config
authorpcarana <pc.moreno2099@gmail.com>
Thu, 14 Feb 2019 15:38:04 +0000 (09:38 -0600)
committerpcarana <pc.moreno2099@gmail.com>
Thu, 14 Feb 2019 15:38:04 +0000 (09:38 -0600)
src/Makefile.am
src/address.c [new file with mode: 0644]
src/address.h [new file with mode: 0644]
src/configuration.c
src/csv.c [new file with mode: 0644]
src/csv.h [new file with mode: 0644]
src/line_file.c [new file with mode: 0644]
src/line_file.h [new file with mode: 0644]
src/main.c
test/Makefile.am
test/address_test.c [new file with mode: 0644]

index 00eca9afc2baafeb14e1ad8844eca93851cc21d6..d9764d9d30be496eac68fab726245e1b8a90d322 100644 (file)
@@ -4,8 +4,11 @@ AM_LDFLAGS =
 bin_PROGRAMS = rtr_server
 
 rtr_server_SOURCES = main.c
+rtr_server_SOURCES += address.c address.h
 rtr_server_SOURCES += common.c common.h
 rtr_server_SOURCES += configuration.c configuration.h
+rtr_server_SOURCES += csv.c csv.h
+rtr_server_SOURCES += line_file.c line_file.h
 
 rtr_server_SOURCES += rtr/pdu_handler.c rtr/pdu_handler.h
 rtr_server_SOURCES += rtr/pdu.c rtr/pdu.h
diff --git a/src/address.c b/src/address.c
new file mode 100644 (file)
index 0000000..f1b2a9c
--- /dev/null
@@ -0,0 +1,176 @@
+#include "address.h"
+
+#include <err.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <arpa/inet.h> /* inet_ntop */
+
+static char const *
+addr2str4(struct in_addr *addr, char *buffer)
+{
+       return inet_ntop(AF_INET, addr, buffer, INET_ADDRSTRLEN);
+}
+
+static char const *
+addr2str6(struct in6_addr *addr, char *buffer)
+{
+       return inet_ntop(AF_INET6, addr, buffer, INET6_ADDRSTRLEN);
+}
+
+static int const
+str2addr4(const char *addr, struct in_addr *dst)
+{
+       if (!inet_pton(AF_INET, addr, dst))
+               return -EINVAL;
+       return 0;
+}
+
+static int const
+str2addr6(const char *addr, struct in6_addr *dst)
+{
+       if (!inet_pton(AF_INET6, addr, dst))
+               return -EINVAL;
+       return 0;
+}
+
+/*
+ * Returns a mask you can use to extract the suffix bits of a 32-bit unsigned
+ * number whose prefix lengths @prefix_len.
+ * For example: Suppose that your number is 192.0.2.0/24.
+ * u32_suffix_mask(24) returns 0.0.0.255.
+ *
+ * The result is in host byte order.
+ */
+static uint32_t
+u32_suffix_mask(unsigned int prefix_len)
+{
+       /* `a >> 32` is undefined if `a` is 32 bits. */
+       return (prefix_len < 32) ? (0xFFFFFFFFu >> prefix_len) : 0;
+}
+
+/**
+ * Same as u32_suffix_mask(), except the result is in network byte order
+ * ("be", for "big endian").
+ */
+static uint32_t
+be32_suffix_mask(unsigned int prefix_len)
+{
+       return htonl(u32_suffix_mask(prefix_len));
+}
+
+static void
+ipv6_suffix_mask(unsigned int prefix_len, struct in6_addr *result)
+{
+       if (prefix_len < 32) {
+               result->s6_addr32[0] |= be32_suffix_mask(prefix_len);
+               result->s6_addr32[1] = 0xFFFFFFFFu;
+               result->s6_addr32[2] = 0xFFFFFFFFu;
+               result->s6_addr32[3] = 0xFFFFFFFFu;
+       } else if (prefix_len < 64) {
+               result->s6_addr32[1] |= be32_suffix_mask(prefix_len - 32);
+               result->s6_addr32[2] = 0xFFFFFFFFu;
+               result->s6_addr32[3] = 0xFFFFFFFFu;
+       } else if (prefix_len < 96) {
+               result->s6_addr32[2] |= be32_suffix_mask(prefix_len - 64);
+               result->s6_addr32[3] = 0xFFFFFFFFu;
+       } else {
+               result->s6_addr32[3] |= be32_suffix_mask(prefix_len - 96);
+       }
+}
+
+int
+prefix4_decode(const char *str, struct ipv4_prefix *result)
+{
+       int error;
+
+       if (str == NULL) {
+               err(-EINVAL, "Null string received, can't decode IPv4 prefix");
+               return -EINVAL;
+       }
+
+       error = str2addr4(str, &result->addr);
+       if (error) {
+               err(error, "Invalid IPv4 prefix %s", str);
+               return error;
+       }
+
+       return 0;
+}
+
+int
+prefix6_decode(const char *str, struct ipv6_prefix *result)
+{
+       int error;
+
+       if (str == NULL) {
+               err(-EINVAL, "Null string received, can't decode IPv6 prefix");
+               return -EINVAL;
+       }
+
+       error = str2addr6(str, &result->addr);
+       if (error) {
+               err(error, "Invalid IPv6 prefix %s", str);
+               return error;
+       }
+
+       return 0;
+ }
+
+int
+prefix_length_decode (const char *text, unsigned int *dst, int max_value)
+{
+       unsigned long len;
+
+       if (text == NULL) {
+               err(-EINVAL, "Null string received, can't decode prefix length");
+               return -EINVAL;
+       }
+
+       errno = 0;
+       len = strtoul(text, NULL, 10);
+       if (errno) {
+               err(errno, "Invalid prefix length '%s': %s", text, strerror(errno));
+               return -EINVAL;
+       }
+       /* An underflow or overflow will be considered here */
+       if (len < 0 || max_value < len) {
+               err(-EINVAL, "Prefix length (%ld) is out of bounds (0-%d).",
+                   len, max_value);
+               return -EINVAL;
+       }
+       *dst = (unsigned int) len;
+       return 0;
+}
+
+int
+prefix4_validate (struct ipv4_prefix *prefix)
+{
+       char buffer[INET_ADDRSTRLEN];
+
+       if ((prefix->addr.s_addr & be32_suffix_mask(prefix->len)) != 0) {
+               err(-EINVAL, "IPv4 prefix %s/%u has enabled suffix bits.",
+                       addr2str4(&prefix->addr, buffer), prefix->len);
+               return -EINVAL;
+       }
+       return 0;
+}
+
+int
+prefix6_validate (struct ipv6_prefix *prefix)
+{
+       struct in6_addr suffix;
+       char buffer[INET6_ADDRSTRLEN];
+
+       memset(&suffix, 0, sizeof(suffix));
+       ipv6_suffix_mask(prefix->len, &suffix);
+       if (   (prefix->addr.s6_addr32[0] & suffix.s6_addr32[0])
+               || (prefix->addr.s6_addr32[1] & suffix.s6_addr32[1])
+               || (prefix->addr.s6_addr32[2] & suffix.s6_addr32[2])
+               || (prefix->addr.s6_addr32[3] & suffix.s6_addr32[3])) {
+               err(-EINVAL, "IPv6 prefix %s/%u has enabled suffix bits.",
+                       addr2str6(&prefix->addr, buffer), prefix->len);
+               return -EINVAL;
+       }
+       return 0;
+}
diff --git a/src/address.h b/src/address.h
new file mode 100644 (file)
index 0000000..1969f62
--- /dev/null
@@ -0,0 +1,24 @@
+#ifndef SRC_ADDRESS_H_
+#define SRC_ADDRESS_H_
+
+#include <netinet/in.h>
+
+struct ipv4_prefix {
+       struct in_addr addr;
+       unsigned int len;
+};
+
+struct ipv6_prefix {
+       struct in6_addr addr;
+       unsigned int len;
+};
+
+int prefix4_decode(const char *, struct ipv4_prefix *);
+int prefix6_decode(const char *, struct ipv6_prefix *);
+
+int prefix_length_decode(const char *, unsigned int *, int);
+
+int prefix4_validate (struct ipv4_prefix *);
+int prefix6_validate (struct ipv6_prefix *);
+
+#endif /* SRC_ADDRESS_H_ */
index 3c99482a80c459301d3440037ef45eb6707f5937..69df376d2a77e4a644fe0a831177243d7e3ef452 100644 (file)
@@ -4,27 +4,34 @@
 #include <err.h>
 #include <errno.h>
 #include <jansson.h>
+#include <stdbool.h>
 #include <string.h>
 
 #include "common.h"
+#include "csv.h"
 
 #define OPTNAME_LISTEN         "listen"
 #define OPTNAME_LISTEN_ADDRESS "address"
 #define OPTNAME_LISTEN_PORT    "port"
+#define OPTNAME_VRPS   "vrps"
 
 #define DEFAULT_ADDR           NULL
 #define DEFAULT_PORT           "323"
+#define DEFAULT_VRPS           NULL
 
 struct rtr_config {
        /** The listener address of the RTR server. */
        struct addrinfo *address;
        /** Stored aside only for printing purposes. */
        char *port;
+       /** VRPs (Validated ROA Payload) location */
+       char *vrps;
 } config;
 
 static int handle_json(json_t *);
 static int json_get_string(json_t *, char const *, char *, char const **);
 static int init_addrinfo(char const *, char const *);
+static int init_vrps_db(char const *);
 
 int
 config_init(char const *json_file_path)
@@ -33,6 +40,10 @@ config_init(char const *json_file_path)
        json_error_t json_error;
        int error;
 
+       /*
+        * TODO What's the point of a default start if there's
+        * no vrps input?
+        */
        if (json_file_path == NULL)
                return init_addrinfo(DEFAULT_ADDR, DEFAULT_PORT);
 
@@ -57,6 +68,8 @@ config_cleanup(void)
                freeaddrinfo(config.address);
        if (config.port != NULL)
                free(config.port);
+       if (config.vrps != NULL)
+               free(config.vrps);
 }
 
 static int
@@ -65,6 +78,7 @@ handle_json(json_t *root)
        json_t *listen;
        char const *address;
        char const *port;
+       char const *vrps;
        int error;
 
        if (!json_is_object(root)) {
@@ -95,6 +109,15 @@ handle_json(json_t *root)
                port = DEFAULT_PORT;
        }
 
+       error = json_get_string(root, OPTNAME_VRPS,
+                           DEFAULT_VRPS, &vrps);
+       if (error)
+               return error;
+
+       error = init_vrps_db(vrps);
+       if (error)
+               return error;
+
        return init_addrinfo(address, port);
 }
 
@@ -119,6 +142,24 @@ json_get_string(json_t *parent, char const *name, char *default_value,
        return 0;
 }
 
+static int
+init_vrps_db(char const *vrps_location)
+{
+       /* FIXME Complete me! */
+       int error;
+
+       if (vrps_location == NULL || strlen(vrps_location) < 1) {
+               warnx("VRPs location must be set");
+               return -EINVAL;
+       }
+
+       error = parse_file(vrps_location);
+       if (error)
+               return error; /* Error msg already printed. */
+
+       return 0;
+}
+
 static int
 init_addrinfo(char const *hostname, char const *service)
 {
@@ -153,3 +194,9 @@ config_get_server_port(void)
 {
        return config.port;
 }
+
+char const *
+config_get_vrps(void)
+{
+       return config.vrps;
+}
diff --git a/src/csv.c b/src/csv.c
new file mode 100644 (file)
index 0000000..bd8d051
--- /dev/null
+++ b/src/csv.c
@@ -0,0 +1,206 @@
+#include "csv.h"
+
+#include <err.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "address.h"
+#include "line_file.h"
+
+struct csv_data {
+       char *asn;
+       char *prefix;
+       int max_length;
+       char *trust_anchor;
+};
+
+/* @ext must include the period. */
+static bool
+location_has_extension(char const *location, char const *ext)
+{
+       size_t ext_len, loc_len;
+       int cmp;
+
+       ext_len = strlen(ext);
+       loc_len = strlen(location);
+       if (loc_len < ext_len)
+               return false;
+
+       cmp = strncmp(location + loc_len - ext_len, ext, ext_len);
+       return cmp == 0;
+}
+
+static int
+parse_asn(char *text)
+{
+       if (text == NULL)
+               return -EINVAL;
+       return 0;
+}
+
+static int
+parse_prefix4(char *text, struct ipv4_prefix *prefixv4)
+{
+       if (text == NULL)
+               return -EINVAL;
+       return prefix4_decode(text, prefixv4);
+}
+
+static int
+parse_prefix6(char *text, struct ipv6_prefix *prefixv6)
+{
+       if (text == NULL)
+               return -EINVAL;
+       return prefix6_decode(text, prefixv6);
+}
+
+static int
+parse_prefix_length(char *text, unsigned int *value, int max_value)
+{
+       if (text == NULL)
+               return -EINVAL;
+       return prefix_length_decode(text, value, max_value);
+}
+
+static int
+add_vrp(char *line)
+{
+       struct ipv4_prefix prefixv4;
+       struct ipv6_prefix prefixv6;
+       unsigned int prefix_length, max_prefix_length;
+       int error;
+       bool isv4;
+       char *token, *line_copy;
+
+       line_copy = malloc(strlen(line) + 1);
+       if (line_copy == NULL) {
+               error = -ENOMEM;
+               err(error, "Out of memory allocating CSV line copy");
+       }
+       strcpy(line_copy, line);
+
+       error = 0;
+
+       /* First column: ASN in format "AS###" */
+       token = strtok(line_copy, ",");
+       error = parse_asn(token);
+       if (error)
+               goto error;
+
+       /* Second column (first part): Prefix in string format */
+       token = strtok(NULL, "/");
+       isv4 = strchr(token, ':') == NULL;
+       if (isv4)
+               error = parse_prefix4(token, &prefixv4);
+       else
+               error = parse_prefix6(token, &prefixv6);
+
+       if (error)
+               goto error;
+
+       /* Second column (second part): Prefix length in numeric format */
+       token = strtok(NULL, ",");
+       error = parse_prefix_length(token, &prefix_length, isv4 ? 32 : 128);
+       if (error)
+               goto error;
+
+       /* Third column: Prefix max length in numeric format */
+       token = strtok(NULL, ",");
+       error = parse_prefix_length(token, &max_prefix_length, isv4 ? 32 : 128);
+       if (error)
+               goto error;
+
+       /* Now validate the prefix */
+       if (isv4) {
+               prefixv4.len = prefix_length;
+               error = prefix4_validate(&prefixv4);
+       } else {
+               prefixv6.len = prefix_length;
+               error = prefix6_validate(&prefixv6);
+       }
+       if (error)
+               goto error;
+
+       if (prefix_length > max_prefix_length) {
+               error = -EINVAL;
+               err(error, "Prefix length is greater than max prefix length [%u > %u]",
+                   prefix_length, max_prefix_length);
+       }
+
+       /* TODO Now store the values in memory */
+error:
+       return error;
+}
+
+static int
+read_vrps(struct line_file *lfile)
+{
+       char *line;
+       int current_line;
+       int error;
+
+       /* First line is expected to be the header, ignore it */
+       current_line = 1;
+       error = lfile_read(lfile, &line);
+       if (error) {
+               err(error, "Error at first line, stop processing CSV file.");
+               return error;
+       }
+       if (line == NULL) {
+               error = -EINVAL;
+               err(error, "Empty file, stop processing.");
+               return error;
+       }
+       do {
+               ++current_line;
+               error = lfile_read(lfile, &line);
+               if (error) {
+                       err(error, "Error at line %d, stop processing file.", current_line);
+                       if (line != NULL)
+                               free(line);
+                       return error;
+               }
+               if (line == NULL) {
+                       free(line);
+                       return 0;
+               }
+               if (strcmp(line, "") == 0) {
+                       warn("There's nothing at line %d, ignoring.", current_line);
+                       continue;
+               }
+
+               error = add_vrp(line);
+               if (error) {
+                       free(line);
+                       return error;
+               }
+       } while (true);
+}
+
+int
+parse_file(char const *location)
+{
+       struct line_file *lfile;
+       int error;
+
+       if (!location_has_extension(location, ".csv")) {
+               warn("%s isn't a CSV file", location);
+               error = -EINVAL;
+               goto end1;
+       }
+
+       error = lfile_open(location, &lfile);
+       if (error)
+               goto end1; /* Error msg already printed. */
+
+       error = read_vrps(lfile);
+       if (error)
+               goto end2;
+
+end2:
+       lfile_close(lfile);
+end1:
+       return error;
+}
diff --git a/src/csv.h b/src/csv.h
new file mode 100644 (file)
index 0000000..8fc6a34
--- /dev/null
+++ b/src/csv.h
@@ -0,0 +1,6 @@
+#ifndef SRC_CSV_H_
+#define SRC_CSV_H_
+
+int parse_file(char const *);
+
+#endif /* SRC_CSV_H_ */
diff --git a/src/line_file.c b/src/line_file.c
new file mode 100644 (file)
index 0000000..1742bc6
--- /dev/null
@@ -0,0 +1,165 @@
+#include "line_file.h"
+
+#include <err.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+struct line_file {
+       FILE *file;
+       const char *file_name;
+       size_t offset;
+};
+
+/*
+ * @file_name is expected to outlive the lfile.
+ */
+int
+lfile_open(const char *file_name, struct line_file **result)
+{
+       struct line_file *lfile;
+       int error;
+
+       lfile = malloc(sizeof(struct line_file));
+       if (lfile == NULL)
+               return -ENOMEM;
+
+       lfile->file = fopen(file_name, "r");
+       if (lfile->file == NULL) {
+               error = errno;
+               free(lfile);
+               return error;
+       }
+       lfile->file_name = file_name;
+       lfile->offset = 0;
+
+       *result = lfile;
+       return 0;
+}
+
+void
+lfile_close(struct line_file *lf)
+{
+       if (fclose(lf->file) == -1)
+               err(errno, "fclose() failed: %s", strerror(errno));
+       free(lf);
+}
+
+/*
+ * On success, places the string in *result.
+ * On failure, returns error code.
+ * On EOF reached, returns zero but nullifies result.
+ *
+ * @result is allocated in the heap.
+ */
+int
+lfile_read(struct line_file *lfile, char **result)
+{
+       char *string;
+       size_t alloc_len;
+       ssize_t len;
+       ssize_t i;
+       int error;
+
+       /*
+        * Note to myself:
+        *
+        * getline() is very convoluted. I really don't like it. I'm actually
+        * considering getting rid of it and pulling off something that doesn't
+        * seem like it was designed by an alien, but it doesn't warrant going
+        * that far yet. Do not read its Linux man page; it didn't answer my
+        * questions. Go straight to POSIX instead.
+        *
+        * - If the file is empty, or all that's left is an empty line, it
+        *   (confusingly) returns -1. errno will be 0, feof() should return
+        *   1, ferror() should return 0.
+        * - The fact that it returns the newline in the buffer is puzzling,
+        *   because who the fuck wants that nonsense. You will want to remove
+        *   it, BUT DON'T SWEAT IT IF IT'S NOT THERE, because the last line of
+        *   the file might not be newline-terminated.
+        * - The string WILL be NULL-terminated, but the NULL chara will not be
+        *   included in the returned length. BUT IT'S THERE. Don't worry about
+        *   writing past the allocated space on the last line.
+        * - Newline is `\n` according to POSIX, which is good, because RFC 7730
+        *   agrees. You will have to worry about `\r`, though.
+        *
+        * Also, the Linux man page claims the following:
+        *
+        *    [The out] buffer should be freed by the user program even if
+        *    getline() failed.
+        *
+        * This... does not exist in the POSIX spec. But it does make sense
+        * because getline is normally meant to be used repeatedly with a
+        * recycled buffer. (free() is a no-op if its argument is NULL so go
+        * nuts.)
+        */
+
+       string = NULL;
+       alloc_len = 0;
+       len = getline(&string, &alloc_len, lfile->file);
+
+       if (len == -1) {
+               error = errno;
+               free(string);
+               *result = NULL;
+               if (ferror(lfile->file)) {
+                       err(error, "Error while reading file: %s\n", strerror(error));
+                       return error;
+               }
+               if (feof(lfile->file))
+                       return 0;
+
+               error = -EINVAL;
+               err(error, "Supposedly unreachable code reached. ferror:%d feof:%d\n",
+                   ferror(lfile->file), feof(lfile->file));
+               return error;
+       }
+
+       lfile->offset += len;
+
+       /*
+        * Make sure that strlen() matches len.
+        * We should make the best out of the fact that we didn't use fgets(),
+        * after all.
+        */
+       for (i = 0; i < len; i++) {
+               if (string[i] == '\0') {
+                       error = -EINVAL;
+                       err(error,
+                           "File '%s' has an illegal null character in its body. Please remove it.\n",
+                           lfile_name(lfile));
+                       free(string);
+                       return error;
+               }
+       }
+
+       if (len >= 2) {
+               if (string[len - 2] == '\r' && string[len - 1] == '\n')
+                       string[len - 2] = '\0';
+       }
+       if (len >= 1) {
+               if (string[len - 1] == '\n')
+                       string[len - 1] = '\0';
+       }
+
+       *result = string;
+       return 0;
+}
+
+FILE *
+lfile_fd(struct line_file *lfile)
+{
+       return lfile->file;
+}
+
+const char *
+lfile_name(struct line_file *lfile)
+{
+       return lfile->file_name;
+}
+
+size_t
+lfile_offset(struct line_file *lfile)
+{
+       return lfile->offset;
+}
diff --git a/src/line_file.h b/src/line_file.h
new file mode 100644 (file)
index 0000000..15b9208
--- /dev/null
@@ -0,0 +1,25 @@
+#ifndef LINE_FILE_H_
+#define LINE_FILE_H_
+
+/*
+ * A "line file" is a text file that you want to read line-by-line.
+ *
+ * Lines are terminated by either CRLF or LF.
+ * (...which is the same as saying "lines are terminated by LF.")
+ */
+
+#include <stddef.h>
+#include <stdio.h>
+
+struct line_file;
+
+int lfile_open(const char *, struct line_file **);
+void lfile_close();
+
+int lfile_read(struct line_file *, char **);
+
+FILE *lfile_fd(struct line_file *);
+const char *lfile_name(struct line_file *);
+size_t lfile_offset(struct line_file *);
+
+#endif /* LINE_FILE_H_ */
index 81998c04c9dc241725551a4938497d93f36a5254..c8e8fb3175655151866ed51b791bcb4dea1dd062 100644 (file)
@@ -32,6 +32,12 @@ main(int argc, char *argv[])
                }
        }
 
+       /* TODO This will be overriden when reading from config file */
+       if (json_file == NULL) {
+               fprintf(stderr, "Missing flag '-f <file name>'\n");
+               return -EINVAL;
+       }
+
        err = config_init(json_file);
        if (err)
                return err;
index 5fefa073c5d122370200b35b8d1e94620c689351..fdacf08948547601ee10939ff859f56012441494 100644 (file)
@@ -10,7 +10,7 @@ AM_CFLAGS = -pedantic -Wall -std=gnu11 -I../src @CHECK_CFLAGS@
 # target.
 MY_LDADD = $(CHECK_LIBS)
 
-check_PROGRAMS = rtr/primitive_reader.test rtr/pdu.test
+check_PROGRAMS = rtr/primitive_reader.test rtr/pdu.test address.test
 TESTS = $(check_PROGRAMS)
 
 rtr_primitive_reader_test_SOURCES = \
@@ -24,3 +24,7 @@ rtr_pdu_test_SOURCES = \
        $(top_builddir)/src/rtr/primitive_reader.c \
        $(top_builddir)/src/rtr/pdu_handler.c
 rtr_pdu_test_LDADD = $(MY_LDADD)
+
+address_test_SOURCES = ../src/address.h
+address_test_SOURCES += address_test.c
+address_test_LDADD = $(MY_LDADD)
diff --git a/test/address_test.c b/test/address_test.c
new file mode 100644 (file)
index 0000000..bb23a85
--- /dev/null
@@ -0,0 +1,73 @@
+#include "address.c"
+
+#include <check.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+START_TEST(load_normal)
+{
+
+}
+END_TEST
+
+static void
+test_get_address_from_string(char *text_prefix)
+{
+       struct ipv4_prefix prefix;
+       const char *result;
+       int error;
+       char buffer[INET_ADDRSTRLEN];
+
+       error = prefix4_decode(text_prefix, &prefix);
+       if (error)
+               return;
+
+       result = addr2str4(&prefix.addr, buffer);
+
+       ck_assert_str_eq(text_prefix, result);
+}
+
+START_TEST(address_test_get_addr)
+{
+       char *text;
+       text = "198.248.146.0";
+
+       test_get_address_from_string(text);
+
+}
+END_TEST
+
+Suite *address_load_suite(void)
+{
+       Suite *suite;
+       TCase *core, *test_get_address;
+
+       core = tcase_create("Core");
+       tcase_add_test(core, load_normal);
+
+       test_get_address = tcase_create("test_get_address");
+       tcase_add_test(test_get_address, address_test_get_addr);
+
+       suite = suite_create("address_test()");
+       suite_add_tcase(suite, core);
+       suite_add_tcase(suite, test_get_address);
+
+       return suite;
+}
+
+int main(void)
+{
+       Suite *suite;
+       SRunner *runner;
+       int tests_failed;
+
+       suite = address_load_suite();
+
+       runner = srunner_create(suite);
+       srunner_run_all(runner, CK_NORMAL);
+       tests_failed = srunner_ntests_failed(runner);
+       srunner_free(runner);
+
+       return (tests_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}