236 lines
5.8 KiB
C
236 lines
5.8 KiB
C
/*
|
|
* libifupdown/interface-file.h
|
|
* Purpose: /etc/network/interfaces parser
|
|
*
|
|
* Copyright (c) 2020 Ariadne Conill <ariadne@dereferenced.org>
|
|
*
|
|
* Permission to use, copy, modify, and/or distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* This software is provided 'as is' and without any warranty, express or
|
|
* implied. In no event shall the authors be liable for any damages arising
|
|
* from the use of this software.
|
|
*/
|
|
|
|
#include <ctype.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include "libifupdown/libifupdown.h"
|
|
|
|
/* internally rewrite problematic ifupdown2 tokens to ifupdown-ng equivalents */
|
|
struct remap_token {
|
|
const char *token;
|
|
const char *alternative;
|
|
};
|
|
|
|
static const struct remap_token tokens[] = {
|
|
{"provider", "ppp-provider"}, /* legacy ifupdown, ifupdown2 */
|
|
{"vrf", "vrf-member"},
|
|
};
|
|
|
|
static int
|
|
token_cmp(const void *a, const void *b)
|
|
{
|
|
const char *key = a;
|
|
const struct remap_token *token = b;
|
|
|
|
return strcmp(key, token->token);
|
|
}
|
|
|
|
static char *
|
|
maybe_remap_token(const char *token)
|
|
{
|
|
const struct remap_token *tok = NULL;
|
|
static char tokbuf[4096];
|
|
|
|
tok = bsearch(token, tokens, ARRAY_SIZE(tokens), sizeof(*tokens), token_cmp);
|
|
strlcpy(tokbuf, tok != NULL ? tok->alternative : token, sizeof tokbuf);
|
|
|
|
return tokbuf;
|
|
}
|
|
|
|
bool
|
|
lif_interface_file_parse(struct lif_dict *collection, const char *filename)
|
|
{
|
|
lif_interface_collection_init(collection);
|
|
struct lif_interface *cur_iface = NULL;
|
|
|
|
FILE *f = fopen(filename, "r");
|
|
if (f == NULL)
|
|
return false;
|
|
|
|
char linebuf[4096];
|
|
while (lif_fgetline(linebuf, sizeof linebuf, f) != NULL)
|
|
{
|
|
char *bufp = linebuf;
|
|
char *token = lif_next_token(&bufp);
|
|
|
|
if (!*token || !isalpha(*token))
|
|
continue;
|
|
|
|
if (!strcmp(token, "source"))
|
|
{
|
|
char *source_filename = lif_next_token(&bufp);
|
|
if (!*source_filename)
|
|
goto parse_error;
|
|
|
|
if (!strcmp(filename, source_filename))
|
|
{
|
|
fprintf(stderr, "%s: attempt to source %s would create infinite loop\n",
|
|
filename, source_filename);
|
|
goto parse_error;
|
|
}
|
|
|
|
lif_interface_file_parse(collection, source_filename);
|
|
}
|
|
else if (!strcmp(token, "auto"))
|
|
{
|
|
char *ifname = lif_next_token(&bufp);
|
|
if (!*ifname && cur_iface == NULL)
|
|
goto parse_error;
|
|
else
|
|
{
|
|
cur_iface = lif_interface_collection_find(collection, ifname);
|
|
if (cur_iface == NULL)
|
|
goto parse_error;
|
|
}
|
|
|
|
cur_iface->is_auto = true;
|
|
}
|
|
else if (!strcmp(token, "iface"))
|
|
{
|
|
char *ifname = lif_next_token(&bufp);
|
|
if (!*ifname)
|
|
goto parse_error;
|
|
|
|
cur_iface = lif_interface_collection_find(collection, ifname);
|
|
if (cur_iface == NULL)
|
|
goto parse_error;
|
|
|
|
/* in original ifupdown config, we can have "inet loopback"
|
|
* or "inet dhcp" or such to designate hints. lets pick up
|
|
* those hints here.
|
|
*/
|
|
char *token = lif_next_token(&bufp);
|
|
while (*token)
|
|
{
|
|
if (!strcmp(token, "dhcp"))
|
|
{
|
|
cur_iface->is_dhcp = true;
|
|
lif_dict_add(&cur_iface->vars, "use", strdup("dhcp"));
|
|
}
|
|
else if (!strcmp(token, "ppp"))
|
|
{
|
|
lif_dict_add(&cur_iface->vars, "use", strdup("ppp"));
|
|
}
|
|
else if (!strcmp(token, "inherits"))
|
|
{
|
|
token = lif_next_token(&bufp);
|
|
|
|
if (!*token)
|
|
{
|
|
fprintf(stderr, "%s: inherits without interface\n", filename);
|
|
goto parse_error;
|
|
}
|
|
|
|
if (!lif_interface_collection_inherit(cur_iface, collection, token))
|
|
{
|
|
fprintf(stderr, "%s: could not inherit %s\n", filename, token);
|
|
goto parse_error;
|
|
}
|
|
}
|
|
|
|
token = lif_next_token(&bufp);
|
|
}
|
|
}
|
|
else if (!strcmp(token, "use"))
|
|
{
|
|
char *executor = lif_next_token(&bufp);
|
|
|
|
if (cur_iface == NULL)
|
|
{
|
|
fprintf(stderr, "%s: use '%s' without interface\n", filename, executor);
|
|
goto parse_error;
|
|
}
|
|
|
|
/* pass requires as compatibility env vars to appropriate executors (bridge, bond) */
|
|
if (!strcmp(executor, "dhcp"))
|
|
cur_iface->is_dhcp = true;
|
|
else if (!strcmp(executor, "bridge"))
|
|
cur_iface->is_bridge = true;
|
|
else if (!strcmp(executor, "bond"))
|
|
cur_iface->is_bond = true;
|
|
else if (!strcmp(executor, "static"))
|
|
{
|
|
cur_iface->is_static = true;
|
|
continue;
|
|
}
|
|
else if (!strcmp(executor, "link"))
|
|
continue;
|
|
|
|
lif_dict_add(&cur_iface->vars, token, strdup(executor));
|
|
}
|
|
else if (!strcmp(token, "inherit"))
|
|
{
|
|
token = lif_next_token(&bufp);
|
|
|
|
if (!*token)
|
|
{
|
|
fprintf(stderr, "%s: inherits without interface\n", filename);
|
|
goto parse_error;
|
|
}
|
|
|
|
if (!lif_interface_collection_inherit(cur_iface, collection, token))
|
|
{
|
|
fprintf(stderr, "%s: could not inherit %s\n", filename, token);
|
|
goto parse_error;
|
|
}
|
|
}
|
|
else if (!strcmp(token, "address"))
|
|
{
|
|
char *addr = lif_next_token(&bufp);
|
|
|
|
if (cur_iface == NULL)
|
|
{
|
|
fprintf(stderr, "%s: address '%s' without interface\n", filename, addr);
|
|
goto parse_error;
|
|
}
|
|
|
|
lif_interface_address_add(cur_iface, addr);
|
|
}
|
|
else if (cur_iface != NULL)
|
|
{
|
|
token = maybe_remap_token(token);
|
|
|
|
lif_dict_add(&cur_iface->vars, token, strdup(bufp));
|
|
|
|
/* Check if token looks like <word1>-<word*> and assume <word1> is an addon */
|
|
char *word_end = strchr(token, '-');
|
|
if (word_end)
|
|
{
|
|
/* Copy word1 to not mangle *token */
|
|
char *addon = strndup(token, word_end - token);
|
|
if (lif_dict_add_once(&cur_iface->vars, "use", addon, (lif_dict_cmp_t) strcmp) == NULL)
|
|
free(addon);
|
|
|
|
/* pass requires as compatibility env vars to appropriate executors (bridge, bond) */
|
|
if (!strcmp(addon, "dhcp"))
|
|
cur_iface->is_dhcp = true;
|
|
else if (!strcmp(addon, "bridge"))
|
|
cur_iface->is_bridge = true;
|
|
else if (!strcmp(addon, "bond"))
|
|
cur_iface->is_bond = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
fclose(f);
|
|
return true;
|
|
|
|
parse_error:
|
|
fprintf(stderr, "libifupdown: %s: failed to parse line \"%s\"\n",
|
|
filename, linebuf);
|
|
fclose(f);
|
|
return false;
|
|
}
|