aboutsummaryrefslogtreecommitdiffstats
path: root/include/target.mk
blob: a813ba2d2d87de82b5c9762e8d6000ce424edba2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
#
# Copyright (C) 2007-2008 OpenWrt.org
# Copyright (C) 2016 LEDE Project
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
#

ifneq ($(__target_inc),1)
__target_inc=1

# default device type
DEVICE_TYPE?=router

# Default packages - the really basic set
DEFAULT_PACKAGES:=base-files libc libgcc busybox dropbear mtd uci opkg netifd fstools uclient-fetch logd urandom-seed urngd
# For nas targets
DEFAULT_PACKAGES.nas:=block-mount fdisk lsblk mdadm
# For router targets
DEFAULT_PACKAGES.router:=dnsmasq iptables ip6tables ppp ppp-mod-pppoe firewall odhcpd-ipv6only odhcp6c kmod-ipt-offload
DEFAULT_PACKAGES.bootloader:=

ifneq ($(DUMP),)
  all: dumpinfo
endif

target_conf=$(subst .,_,$(subst -,_,$(subst /,_,$(1))))
ifeq ($(DUMP),)
  PLATFORM_DIR:=$(TOPDIR)/target/linux/$(BOARD)
  SUBTARGET:=$(strip $(foreach subdir,$(patsubst $(PLATFORM_DIR)/%/target.mk,%,$(wildcard $(PLATFORM_DIR)/*/target.mk)),$(if $(CONFIG_TARGET_$(call target_conf,$(BOARD)_$(subdir))),$(subdir))))
else
  PLATFORM_DIR:=${CURDIR}
  ifeq ($(SUBTARGETS),)
    SUBTARGETS:=$(strip $(patsubst $(PLATFORM_DIR)/%/target.mk,%,$(wildcard $(PLATFORM_DIR)/*/target.mk)))
  endif
endif

TARGETID:=$(BOARD)$(if $(SUBTARGET),/$(SUBTARGET))
PLATFORM_SUBDIR:=$(PLATFORM_DIR)$(if $(SUBTARGET),/$(SUBTARGET))

ifneq ($(TARGET_BUILD),1)
  ifndef DUMP
    include $(PLATFORM_DIR)/Makefile
    ifneq ($(PLATFORM_DIR),$(PLATFORM_SUBDIR))
      include $(PLATFORM_SUBDIR)/target.mk
    endif
  endif
else
  ifneq ($(SUBTARGET),)
    -include ./$(SUBTARGET)/target.mk
  endif
endif

ifneq ($(filter 4.9,$(KERNEL_PATCHVER)),)
  DEFAULT_PACKAGES.router:=$(filter-out kmod-ipt-offload,$(DEFAULT_PACKAGES.router))
endif

# Add device specific packages (here below to allow device type set from subtarget)
DEFAULT_PACKAGES += $(DEFAULT_PACKAGES.$(DEVICE_TYPE))

filter_packages = $(filter-out -% $(patsubst -%,%,$(filter -%,$(1))),$(1))
extra_packages = $(if $(filter wpad-mini wpad-basic wpad nas,$(1)),iwinfo)

define ProfileDefault
  NAME:=
  PRIORITY:=
  PACKAGES:=
endef

ifndef Profile
define Profile
  $(eval $(call ProfileDefault))
  $(eval $(call Profile/$(1)))
  dumpinfo : $(call shexport,Profile/$(1)/Description)
  PACKAGES := $(filter-out -%,$(PACKAGES))
  DUMPINFO += \
	echo "Target-Profile: $(1)"; \
	$(if $(PRIORITY), echo "Target-Profile-Priority: $(PRIORITY)"; ) \
	echo "Target-Profile-Name: $(NAME)"; \
	echo "Target-Profile-Packages: $(PACKAGES) $(call extra_packages,$(DEFAULT_PACKAGES) $(PACKAGES))"; \
	echo "Target-Profile-Description:"; \
	echo "$$$$$$$$$(call shvar,Profile/$(1)/Description)"; \
	echo "@@"; \
	echo;
endef
endif

ifneq ($(PLATFORM_DIR),$(PLATFORM_SUBDIR))
  define IncludeProfiles
    -include $(sort $(wildcard $(PLATFORM_DIR)/profiles/*.mk))
    -include $(sort $(wildcard $(PLATFORM_SUBDIR)/profiles/*.mk))
  endef
else
  define IncludeProfiles
    -include $(sort $(wildcard $(PLATFORM_DIR)/profiles/*.mk))
  endef
endif

PROFILE?=$(call qstrip,$(CONFIG_TARGET_PROFILE))

ifeq ($(TARGET_BUILD),1)
  ifneq ($(DUMP),)
    $(eval $(call IncludeProfiles))
  endif
endif

ifneq ($(TARGET_BUILD)$(if $(DUMP),,1),)
  include $(INCLUDE_DIR)/kernel-version.mk
endif

GENERIC_PLATFORM_DIR := $(TOPDIR)/target/linux/generic
GENERIC_BACKPORT_DIR := $(GENERIC_PLATFORM_DIR)/backport$(if $(wildcard $(GENERIC_PLATFORM_DIR)/backport-$(KERNEL_PATCHVER)),-$(KERNEL_PATCHVER))
GENERIC_PATCH_DIR := $(GENERIC_PLATFORM_DIR)/pending$(if $(wildcard $(GENERIC_PLATFORM_DIR)/pending-$(KERNEL_PATCHVER)),-$(KERNEL_PATCHVER))
GENERIC_HACK_DIR := $(GENERIC_PLATFORM_DIR)/hack$(if $(wildcard $(GENERIC_PLATFORM_DIR)/hack-$(KERNEL_PATCHVER)),-$(KERNEL_PATCHVER))
GENERIC_FILES_DIR := $(foreach dir,$(wildcard $(GENERIC_PLATFORM_DIR)/files $(GENERIC_PLATFORM_DIR)/files-$(KERNEL_PATCHVER)),"$(dir)")

__config_name_list = $(1)/config-$(KERNEL_PATCHVER) $(1)/config-default
__config_list = $(firstword $(wildcard $(call __config_name_list,$(1))))
find_kernel_config=$(if $(__config_list),$(__config_list),$(lastword $(__config_name_list)))

GENERIC_LINUX_CONFIG = $(call find_kernel_config,$(GENERIC_PLATFORM_DIR))
LINUX_TARGET_CONFIG = $(call find_kernel_config,$(PLATFORM_DIR))
ifneq ($(PLATFORM_DIR),$(PLATFORM_SUBDIR))
  LINUX_SUBTARGET_CONFIG = $(call find_kernel_config,$(PLATFORM_SUBDIR))
endif

# config file list used for compiling
LINUX_KCONFIG_LIST = $(wildcard $(GENERIC_LINUX_CONFIG) $(LINUX_TARGET_CONFIG) $(LINUX_SUBTARGET_CONFIG) $(TOPDIR)/env/kernel-config)

# default config list for reconfiguring
# defaults to subtarget if subtarget exists and target does not
# defaults to target otherwise
USE_SUBTARGET_CONFIG = $(if $(wildcard $(LINUX_TARGET_CONFIG)),,$(if $(LINUX_SUBTARGET_CONFIG),1))

LINUX_RECONFIG_LIST = $(wildcard $(GENERIC_LINUX_CONFIG) $(LINUX_TARGET_CONFIG) $(if $(USE_SUBTARGET_CONFIG),$(LINUX_SUBTARGET_CONFIG)))
LINUX_RECONFIG_TARGET = $(if $(USE_SUBTARGET_CONFIG),$(LINUX_SUBTARGET_CONFIG),$(LINUX_TARGET_CONFIG))

# select the config file to be changed by kernel_menuconfig/kernel_oldconfig
ifeq ($(CONFIG_TARGET),platform)
  LINUX_RECONFIG_LIST = $(wildcard $(GENERIC_LINUX_CONFIG) $(LINUX_TARGET_CONFIG))
  LINUX_RECONFIG_TARGET = $(LINUX_TARGET_CONFIG)
endif
ifeq ($(CONFIG_TARGET),subtarget)
  LINUX_RECONFIG_LIST = $(wildcard $(GENERIC_LINUX_CONFIG) $(LINUX_TARGET_CONFIG) $(LINUX_SUBTARGET_CONFIG))
  LINUX_RECONFIG_TARGET = $(LINUX_SUBTARGET_CONFIG)
endif
ifeq ($(CONFIG_TARGET),subtarget_platform)
  LINUX_RECONFIG_LIST = $(wildcard $(GENERIC_LINUX_CONFIG) $(LINUX_SUBTARGET_CONFIG) $(LINUX_TARGET_CONFIG))
  LINUX_RECONFIG_TARGET = $(LINUX_TARGET_CONFIG)
endif
ifeq ($(CONFIG_TARGET),env)
  LINUX_RECONFIG_LIST = $(LINUX_KCONFIG_LIST)
  LINUX_RECONFIG_TARGET = $(TOPDIR)/env/kernel-config
endif

__linux_confcmd = $(SCRIPT_DIR)/kconfig.pl $(2) $(patsubst %,+,$(wordlist 2,9999,$(1))) $(1)

LINUX_CONF_CMD = $(call __linux_confcmd,$(LINUX_KCONFIG_LIST),)
LINUX_RECONF_CMD = $(call __linux_confcmd,$(LINUX_RECONFIG_LIST),)
LINUX_RECONF_DIFF = $(call __linux_confcmd,$(filter-out $(LINUX_RECONFIG_TARGET),$(LINUX_RECONFIG_LIST)),'>')

ifeq ($(DUMP),1)
  BuildTarget=$(BuildTargets/DumpCurrent)

  CPU_CFLAGS = -Os -pipe
  ifneq ($(findstring mips,$(ARCH)),)
    ifneq ($(findstring mips64,$(ARCH)),)
      CPU_TYPE ?= mips64
    else
      CPU_TYPE ?= mips32
    endif
    CPU_CFLAGS += -mno-branch-likely
    CPU_CFLAGS_mips32 = -mips32 -mtune=mips32
    CPU_CFLAGS_mips64 = -mips64 -mtune=mips64 -mabi=64
    CPU_CFLAGS_24kc = -mips32r2 -mtune=24kc
    CPU_CFLAGS_74kc = -mips32r2 -mtune=74kc
    CPU_CFLAGS_octeonplus = -march=octeon+ -mabi=64
  endif
  ifeq ($(ARCH),i386)
    CPU_TYPE ?= pentium
    CPU_CFLAGS_pentium = -march=pentium-mmx
    CPU_CFLAGS_pentium4 = -march=pentium4
  endif
  ifneq ($(findstring arm,$(ARCH)),)
    CPU_TYPE ?= xscale
  endif
  ifeq ($(ARCH),powerpc)
    CPU_CFLAGS_603e:=-mcpu=603e
    CPU_CFLAGS_8540:=-mcpu=8540
    CPU_CFLAGS_405:=-mcpu=405
    CPU_CFLAGS_440:=-mcpu=440
    CPU_CFLAGS_464fp:=-mcpu=464fp
  endif
  ifeq ($(ARCH),powerpc64)
    CPU_TYPE ?= powerpc64
    CPU_CFLAGS_powerpc64:=-mcpu=powerpc64
  endif
  ifeq ($(ARCH),sparc)
    CPU_TYPE = sparc
    CPU_CFLAGS_ultrasparc = -mcpu=ultrasparc
  endif
  ifeq ($(ARCH),aarch64)
    CPU_TYPE ?= generic
    CPU_CFLAGS_generic = -mcpu=generic
    CPU_CFLAGS_cortex-a53 = -mcpu=cortex-a53
  endif
  ifeq ($(ARCH),arc)
    CPU_TYPE ?= arc700
    CPU_CFLAGS += -matomic
    CPU_CFLAGS_arc700 = -mcpu=arc700
    CPU_CFLAGS_archs = -mcpu=archs
  endif
  ifneq ($(CPU_TYPE),)
    ifndef CPU_CFLAGS_$(CPU_TYPE)
      $(warning CPU_TYPE "$(CPU_TYPE)" doesn't correspond to a known type)
    endif
  endif
  DEFAULT_CFLAGS=$(strip $(CPU_CFLAGS) $(CPU_CFLAGS_$(CPU_TYPE)) $(CPU_CFLAGS_$(CPU_SUBTYPE)))

  ifneq ($(BOARD),)
    TMP_CONFIG:=$(TMP_DIR)/.kconfig-$(call target_conf,$(TARGETID))
    $(TMP_CONFIG): $(LINUX_KCONFIG_LIST)
		$(LINUX_CONF_CMD) > $@ || rm -f $@
    -include $(TMP_CONFIG)
    .SILENT: $(TMP_CONFIG)
    .PRECIOUS: $(TMP_CONFIG)

    ifdef KERNEL_TESTING_PATCHVER
      FEATURES += testing-kernel
    endif
    ifneq ($(CONFIG_OF),)
      FEATURES += dt
    endif
    ifneq ($(CONFIG_GENERIC_GPIO)$(CONFIG_GPIOLIB),)
      FEATURES += gpio
    endif
    ifneq ($(CONFIG_PCI),)
      FEATURES += pci
    endif
    ifneq ($(CONFIG_PCIEPORTBUS),)
      FEATURES += pcie
    endif
    ifneq ($(CONFIG_USB)$(CONFIG_USB_SUPPORT),)
      ifneq ($(CONFIG_USB_ARCH_HAS_HCD)$(CONFIG_USB_EHCI_HCD),)
        FEATURES += usb
      endif
    endif
    ifneq ($(CONFIG_PCMCIA)$(CONFIG_PCCARD),)
      FEATURES += pcmcia
    endif
    ifneq ($(CONFIG_VGA_CONSOLE)$(CONFIG_FB),)
      FEATURES += display
    endif
    ifneq ($(CONFIG_RTC_CLASS),)
      FEATURES += rtc
    endif
    ifneq ($(CONFIG_VIRTIO),)
      FEATURES += virtio
    endif
    ifneq ($(CONFIG_CPU_MIPS32_R2),)
      FEATURES += mips16
    endif
    FEATURES += $(foreach v,6 7,$(if $(CONFIG_CPU_V$(v)),arm_v$(v)))

    # remove duplicates
    FEATURES:=$(sort $(FEATURES))
  endif
endif

CUR_SUBTARGET:=$(SUBTARGET)
ifeq ($(SUBTARGETS),)
  CUR_SUBTARGET := default
endif

define BuildTargets/DumpCurrent
  .PHONY: dumpinfo
  dumpinfo : export DESCRIPTION=$$(Target/Description)
  dumpinfo:
	@echo 'Target: $(TARGETID)'; \
	 echo 'Target-Board: $(BOARD)'; \
	 echo 'Target-Name: $(BOARDNAME)$(if $(SUBTARGETS),$(if $(SUBTARGET),))'; \
	 echo 'Target-Arch: $(ARCH)'; \
	 echo 'Target-Arch-Packages: $(if $(ARCH_PACKAGES),$(ARCH_PACKAGES),$(ARCH)$(if $(CPU_TYPE),_$(CPU_TYPE))$(if $(CPU_SUBTYPE),_$(CPU_SUBTYPE)))'; \
	 echo 'Target-Features: $(FEATURES)'; \
	 echo 'Target-Depends: $(DEPENDS)'; \
	 echo 'Target-Optimization: $(if $(CFLAGS),$(CFLAGS),$(DEFAULT_CFLAGS))'; \
	 echo 'CPU-Type: $(CPU_TYPE)$(if $(CPU_SUBTYPE),+$(CPU_SUBTYPE))'; \
	 echo 'Linux-Version: $(LINUX_VERSION)'; \
	$(if $(LINUX_TESTING_VERSION),echo 'Linux-Testing-Version: $(LINUX_TESTING_VERSION)';) \
	 echo 'Linux-Release: $(LINUX_RELEASE)'; \
	 echo 'Linux-Kernel-Arch: $(LINUX_KARCH)'; \
	$(if $(SUBTARGET),,$(if $(DEFAULT_SUBTARGET), echo 'Default-Subtarget: $(DEFAULT_SUBTARGET)'; )) \
	 echo 'Target-Description:'; \
	 echo "$$$$DESCRIPTION"; \
	 echo '@@'; \
	 echo 'Default-Packages: $(DEFAULT_PACKAGES) $(call extra_packages,$(DEFAULT_PACKAGES))'; \
	 $(DUMPINFO)
	$(if $(CUR_SUBTARGET),$(SUBMAKE) -r --no-print-directory -C image -s DUMP=1 SUBTARGET=$(CUR_SUBTARGET))
	$(if $(SUBTARGET),,@$(foreach SUBTARGET,$(SUBTARGETS),$(SUBMAKE) -s DUMP=1 SUBTARGET=$(SUBTARGET); ))
endef

include $(INCLUDE_DIR)/kernel.mk
ifeq ($(TARGET_BUILD),1)
  include $(INCLUDE_DIR)/kernel-build.mk
  BuildTarget?=$(BuildKernel)
endif

endif #__target_inc
light .kp { color: #008800 } /* Keyword.Pseudo */ .highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */ .highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */ .highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */ .highlight .na { color: #336699 } /* Name.Attribute */ .highlight .nb { color: #003388 } /* Name.Builtin */ .highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */ .highlight .no { color: #003366; font-weight: bold } /* Name.Constant */ .highlight .nd { color: #555555 } /* Name.Decorator */ .highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */ .highlight .nl { color: #336699; font-style: italic } /* Name.Label */ .highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */ .highlight .py { color: #336699; font-weight: bold } /* Name.Property */ .highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #336699 } /* Name.Variable */ .highlight .ow { color: #008800 } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */ .highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */ .highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */ .highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */ .highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */ .highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */ .highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */ .highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */ .highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */ .highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */ .highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */ .highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */ .highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */ .highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */ .highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */ .highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #336699 } /* Name.Variable.Class */ .highlight .vg { color: #dd7700 } /* Name.Variable.Global */ .highlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .vm { color: #336699 } /* Name.Variable.Magic */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
/*
 * libebtc.c, January 2004
 *
 * Contains the functions with which to make a table in userspace.
 *
 * Author: Bart De Schuymer
 *
 *  This code is stongly inspired on the iptables code which is
 *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
 *
 * 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, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include "include/ebtables_u.h"
#include "include/ethernetdb.h"
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>

static void decrease_chain_jumps(struct ebt_u_replace *replace);
static int iterate_entries(struct ebt_u_replace *replace, int type);

/* The standard names */
const char *ebt_hooknames[NF_BR_NUMHOOKS] =
{
       [NF_BR_PRE_ROUTING]"PREROUTING",
       [NF_BR_LOCAL_IN]"INPUT",
       [NF_BR_FORWARD]"FORWARD",
       [NF_BR_LOCAL_OUT]"OUTPUT",
       [NF_BR_POST_ROUTING]"POSTROUTING",
       [NF_BR_BROUTING]"BROUTING"
};

/* The four target names */
const char* ebt_standard_targets[NUM_STANDARD_TARGETS] =
{
       "ACCEPT",
       "DROP",
       "CONTINUE",
       "RETURN",
};

/* The lists of supported tables, matches, watchers and targets */
struct ebt_u_table *ebt_tables;
struct ebt_u_match *ebt_matches;
struct ebt_u_watcher *ebt_watchers;
struct ebt_u_target *ebt_targets;

/* Find the right structure belonging to a name */
struct ebt_u_target *ebt_find_target(const char *name)
{
       struct ebt_u_target *t = ebt_targets;

       while (t && strcmp(t->name, name))
               t = t->next;
       return t;
}

struct ebt_u_match *ebt_find_match(const char *name)
{
       struct ebt_u_match *m = ebt_matches;

       while (m && strcmp(m->name, name))
               m = m->next;
       return m;
}

struct ebt_u_watcher *ebt_find_watcher(const char *name)
{
       struct ebt_u_watcher *w = ebt_watchers;

       while (w && strcmp(w->name, name))
               w = w->next;
       return w;
}

struct ebt_u_table *ebt_find_table(const char *name)
{
       struct ebt_u_table *t = ebt_tables;

       while (t && strcmp(t->name, name))
               t = t->next;
       return t;
}

/* Prints all registered extensions */
void ebt_list_extensions()
{
       struct ebt_u_table *tbl = ebt_tables;
        struct ebt_u_target *t = ebt_targets;
        struct ebt_u_match *m = ebt_matches;
        struct ebt_u_watcher *w = ebt_watchers;

       PRINT_VERSION;
       printf("Loaded userspace extensions:\n\nLoaded tables:\n");
        while (tbl) {
               printf("%s\n", tbl->name);
                tbl = tbl->next;
       }
       printf("\nLoaded targets:\n");
        while (t) {
               printf("%s\n", t->name);
                t = t->next;
       }
       printf("\nLoaded matches:\n");
        while (m) {
               printf("%s\n", m->name);
                m = m->next;
       }
       printf("\nLoaded watchers:\n");
        while (w) {
               printf("%s\n", w->name);
                w = w->next;
       }
}

/* Get the table from the kernel or from a binary file
 * init: 1 = ask the kernel for the initial contents of a table, i.e. the
 *           way it looks when the table is insmod'ed
 *       0 = get the current data in the table */
int ebt_get_kernel_table(struct ebt_u_replace *replace, int init)
{
       if (!ebt_find_table(replace->name)) {
               ebt_print_error("Bad table name '%s'", replace->name);
               return -1;
       }
       /* Get the kernel's information */
       if (ebt_get_table(replace, init)) {
               if (ebt_errormsg[0] != '\0')
                       return -1;
               ebtables_insmod("ebtables");
               if (ebt_get_table(replace, init)) {
                       ebt_print_error("The kernel doesn't support the ebtables '%s' table", replace->name);
                       return -1;
               }
       }
       return 0;
}

/* Put sane values into a new entry */
void ebt_initialize_entry(struct ebt_u_entry *e)
{
       e->bitmask = EBT_NOPROTO;
       e->invflags = 0;
       e->ethproto = 0;
       strcpy(e->in, "");
       strcpy(e->out, "");
       strcpy(e->logical_in, "");
       strcpy(e->logical_out, "");
       e->m_list = NULL;
       e->w_list = NULL;
       e->t = (struct ebt_entry_target *)ebt_find_target(EBT_STANDARD_TARGET);
       ebt_find_target(EBT_STANDARD_TARGET)->used = 1;
       e->cnt.pcnt = e->cnt.bcnt = e->cnt_surplus.pcnt = e->cnt_surplus.bcnt = 0;

       if (!e->t)
               ebt_print_bug("Couldn't load standard target");
       ((struct ebt_standard_target *)((struct ebt_u_target *)e->t)->t)->verdict = EBT_CONTINUE;
}

/* Free up the memory of the table held in userspace, *replace can be reused */
void ebt_cleanup_replace(struct ebt_u_replace *replace)
{
       int i;
       struct ebt_u_entries *entries;
       struct ebt_cntchanges *cc1, *cc2;
       struct ebt_u_entry *u_e1, *u_e2;

       replace->name[0] = '\0';
       replace->valid_hooks = 0;
       replace->nentries = 0;
       replace->num_counters = 0;
       replace->flags = 0;
       replace->command = 0;
       replace->selected_chain = -1;
       free(replace->filename);
       replace->filename = NULL;
       free(replace->counters);
       replace->counters = NULL;

       for (i = 0; i < replace->num_chains; i++) {
               if (!(entries = replace->chains[i]))
                       continue;
               u_e1 = entries->entries->next;
               while (u_e1 != entries->entries) {
                       ebt_free_u_entry(u_e1);
                       u_e2 = u_e1->next;
                       free(u_e1);
                       u_e1 = u_e2;
               }
               free(entries->entries);
               free(entries);
               replace->chains[i] = NULL;
       }
       cc1 = replace->cc->next;
       while (cc1 != replace->cc) {
               cc2 = cc1->next;
               free(cc1);
               cc1 = cc2;
       }
       replace->cc->next = replace->cc->prev = replace->cc;
}

/* Should be called, e.g., between 2 rule adds */
void ebt_reinit_extensions()
{
       struct ebt_u_match *m;
       struct ebt_u_watcher *w;
       struct ebt_u_target *t;
       int size;

       /* The init functions should determine by themselves whether they are
        * called for the first time or not (when necessary). */
       for (m = ebt_matches; m; m = m->next) {
               if (m->used) {
                       size = EBT_ALIGN(m->size) + sizeof(struct ebt_entry_match);
                       m->m = (struct ebt_entry_match *)malloc(size);
                       if (!m->m)
                               ebt_print_memory();
                       strcpy(m->m->u.name, m->name);
                       m->m->match_size = EBT_ALIGN(m->size);
                       m->used = 0;
               }
               m->flags = 0; /* An error can occur before used is set, while flags is changed. */
               m->init(m->m);
       }
       for (w = ebt_watchers; w; w = w->next) {
               if (w->used) {
                       size = EBT_ALIGN(w->size) + sizeof(struct ebt_entry_watcher);
                       w->w = (struct ebt_entry_watcher *)malloc(size);
                       if (!w->w)
                               ebt_print_memory();
                       strcpy(w->w->u.name, w->name);
                       w->w->watcher_size = EBT_ALIGN(w->size);
                       w->used = 0;
               }
               w->flags = 0;
               w->init(w->w);
       }
       for (t = ebt_targets; t; t = t->next) {
               if (t->used) {
                       size = EBT_ALIGN(t->size) + sizeof(struct ebt_entry_target);
                       t->t = (struct ebt_entry_target *)malloc(size);
                       if (!t->t)
                               ebt_print_memory();
                       strcpy(t->t->u.name, t->name);
                       t->t->target_size = EBT_ALIGN(t->size);
                       t->used = 0;
               }
               t->flags = 0;
               t->init(t->t);
       }
}

/* This doesn't free e, because the calling function might need e->next */
void ebt_free_u_entry(struct ebt_u_entry *e)
{
       struct ebt_u_match_list *m_l, *m_l2;
       struct ebt_u_watcher_list *w_l, *w_l2;

       m_l = e->m_list;
       while (m_l) {
               m_l2 = m_l->next;
               free(m_l->m);
               free(m_l);
               m_l = m_l2;
       }
       w_l = e->w_list;
       while (w_l) {
               w_l2 = w_l->next;
               free(w_l->w);
               free(w_l);
               w_l = w_l2;
       }
       free(e->t);
}

static char *get_modprobe(void)
{
       int procfile;
       char *ret;

       procfile = open(PROC_SYS_MODPROBE, O_RDONLY);
       if (procfile < 0)
               return NULL;

       ret = malloc(1024);
       if (ret) {
               if (read(procfile, ret, 1024) == -1)
                       goto fail;
               /* The kernel adds a '\n' */
               ret[1023] = '\n';
               *strchr(ret, '\n') = '\0';
               close(procfile);
               return ret;
       }
 fail:
       free(ret);
       close(procfile);
       return NULL;
}

char *ebt_modprobe;
/* Try to load the kernel module, analogous to ip_tables.c */
int ebtables_insmod(const char *modname)
{
       char *buf = NULL;
       char *argv[3];

       /* If they don't explicitly set it, read out of /proc */
       if (!ebt_modprobe) {
               buf = get_modprobe();
               if (!buf)
                       return -1;
               ebt_modprobe = buf; /* Keep the value for possible later use */
       }

       switch (fork()) {
       case 0:
               argv[0] = (char *)ebt_modprobe;
               argv[1] = (char *)modname;
               argv[2] = NULL;
               execv(argv[0], argv);

               /* Not usually reached */
               exit(0);
       case -1:
               return -1;

       default: /* Parent */
               wait(NULL);
       }

       return 0;
}

/* Parse the chain name and return a pointer to the chain base.
 * Returns NULL on failure. */
struct ebt_u_entries *ebt_name_to_chain(const struct ebt_u_replace *replace, const char* arg)
{
       int i;
       struct ebt_u_entries *chain;

       for (i = 0; i < replace->num_chains; i++) {
               if (!(chain = replace->chains[i]))
                       continue;
               if (!strcmp(arg, chain->name))
                       return chain;
       }
       return NULL;
}

/* Parse the chain name and return the corresponding chain nr
 * returns -1 on failure */
int ebt_get_chainnr(const struct ebt_u_replace *replace, const char* arg)
{
       int i;

       for (i = 0; i < replace->num_chains; i++) {
               if (!replace->chains[i])
                       continue;
               if (!strcmp(arg, replace->chains[i]->name))
                       return i;
       }
       return -1;
}

     /*
************
************
**COMMANDS**
************
************
     */

/* Change the policy of selected_chain.
 * Handing a bad policy to this function is a bug. */
void ebt_change_policy(struct ebt_u_replace *replace, int policy)
{
       struct ebt_u_entries *entries = ebt_to_chain(replace);

       if (policy < -NUM_STANDARD_TARGETS || policy == EBT_CONTINUE)
               ebt_print_bug("Wrong policy: %d", policy);
       entries->policy = policy;
}

void ebt_delete_cc(struct ebt_cntchanges *cc)
{
       if (cc->type == CNT_ADD) {
               cc->prev->next = cc->next;
               cc->next->prev = cc->prev;
               free(cc);
       }
       cc->type = CNT_DEL;
}

void ebt_empty_chain(struct ebt_u_entries *entries)
{
       struct ebt_u_entry *u_e = entries->entries->next, *tmp;
       while (u_e != entries->entries) {
               ebt_delete_cc(u_e->cc);
               ebt_free_u_entry(u_e);
               tmp = u_e->next;
               free(u_e);
               u_e = tmp;
       }
       entries->entries->next = entries->entries->prev = entries->entries;
       entries->nentries = 0;
}

/* Flush one chain or the complete table
 * If selected_chain == -1 then flush the complete table */
void ebt_flush_chains(struct ebt_u_replace *replace)
{
       int i, numdel;
       struct ebt_u_entries *entries = ebt_to_chain(replace);

       /* Flush whole table */
       if (!entries) {
               if (replace->nentries == 0)
                       return;
               replace->nentries = 0;

               /* Free everything and zero (n)entries */
               for (i = 0; i < replace->num_chains; i++) {
                       if (!(entries = replace->chains[i]))
                               continue;
                       entries->counter_offset = 0;
                       ebt_empty_chain(entries);
               }
               return;
       }

       if (entries->nentries == 0)
               return;
       replace->nentries -= entries->nentries;
       numdel = entries->nentries;

       /* Update counter_offset */
       for (i = replace->selected_chain+1; i < replace->num_chains; i++) {
               if (!(entries = replace->chains[i]))
                       continue;
               entries->counter_offset -= numdel;
       }

       entries = ebt_to_chain(replace);
       ebt_empty_chain(entries);
}

#define OPT_COUNT      0x1000 /* This value is also defined in ebtables.c */
/* Returns the rule number on success (starting from 0), -1 on failure
 *
 * This function expects the ebt_{match,watcher,target} members of new_entry
 * to contain pointers to ebt_u_{match,watcher,target} */
int ebt_check_rule_exists(struct ebt_u_replace *replace,
                         struct ebt_u_entry *new_entry)
{
       struct ebt_u_entry *u_e;
       struct ebt_u_match_list *m_l, *m_l2;
       struct ebt_u_match *m;
       struct ebt_u_watcher_list *w_l, *w_l2;
       struct ebt_u_watcher *w;
       struct ebt_u_target *t = (struct ebt_u_target *)new_entry->t;
       struct ebt_u_entries *entries = ebt_to_chain(replace);
       int i, j, k;

       u_e = entries->entries->next;
       /* Check for an existing rule (if there are duplicate rules,
        * take the first occurance) */
       for (i = 0; i < entries->nentries; i++, u_e = u_e->next) {
               if (u_e->ethproto != new_entry->ethproto)
                       continue;
               if (strcmp(u_e->in, new_entry->in))
                       continue;
               if (strcmp(u_e->out, new_entry->out))
                       continue;
               if (strcmp(u_e->logical_in, new_entry->logical_in))
                       continue;
               if (strcmp(u_e->logical_out, new_entry->logical_out))
                       continue;
               if (new_entry->bitmask & EBT_SOURCEMAC &&
                   memcmp(u_e->sourcemac, new_entry->sourcemac, ETH_ALEN))
                       continue;
               if (new_entry->bitmask & EBT_DESTMAC &&
                   memcmp(u_e->destmac, new_entry->destmac, ETH_ALEN))
                       continue;
               if (new_entry->bitmask != u_e->bitmask ||
                   new_entry->invflags != u_e->invflags)
                       continue;
               if (replace->flags & OPT_COUNT && (new_entry->cnt.pcnt !=
                   u_e->cnt.pcnt || new_entry->cnt.bcnt != u_e->cnt.bcnt))
                       continue;
               /* Compare all matches */
               m_l = new_entry->m_list;
               j = 0;
               while (m_l) {
                       m = (struct ebt_u_match *)(m_l->m);
                       m_l2 = u_e->m_list;
                       while (m_l2 && strcmp(m_l2->m->u.name, m->m->u.name))
                               m_l2 = m_l2->next;
                       if (!m_l2 || !m->compare(m->m, m_l2->m))
                               goto letscontinue;
                       j++;
                       m_l = m_l->next;
               }
               /* Now be sure they have the same nr of matches */
               k = 0;
               m_l = u_e->m_list;
               while (m_l) {
                       k++;
                       m_l = m_l->next;
               }
               if (j != k)
                       continue;

               /* Compare all watchers */
               w_l = new_entry->w_list;
               j = 0;
               while (w_l) {
                       w = (struct ebt_u_watcher *)(w_l->w);
                       w_l2 = u_e->w_list;
                       while (w_l2 && strcmp(w_l2->w->u.name, w->w->u.name))
                               w_l2 = w_l2->next;
                       if (!w_l2 || !w->compare(w->w, w_l2->w))
                               goto letscontinue;
                       j++;
                       w_l = w_l->next;
               }
               k = 0;
               w_l = u_e->w_list;
               while (w_l) {
                       k++;
                       w_l = w_l->next;
               }
               if (j != k)
                       continue;
               if (strcmp(t->t->u.name, u_e->t->u.name))
                       continue;
               if (!t->compare(t->t, u_e->t))
                       continue;
               return i;
letscontinue:;
       }
       return -1;
}

/* Add a rule, rule_nr is the rule to update
 * rule_nr specifies where the rule should be inserted
 * rule_nr > 0 : insert the rule right before the rule_nr'th rule
 *               (the first rule is rule 1)
 * rule_nr < 0 : insert the rule right before the (n+rule_nr+1)'th rule,
 *               where n denotes the number of rules in the chain
 * rule_nr == 0: add a new rule at the end of the chain
 *
 * This function expects the ebt_{match,watcher,target} members of new_entry
 * to contain pointers to ebt_u_{match,watcher,target} and updates these
 * pointers so that they point to ebt_{match,watcher,target}, before adding
 * the rule to the chain. Don't free() the ebt_{match,watcher,target} and
 * don't reuse the new_entry after a successful call to ebt_add_rule() */
void ebt_add_rule(struct ebt_u_replace *replace, struct ebt_u_entry *new_entry, int rule_nr)
{
       int i;
       struct ebt_u_entry *u_e;
       struct ebt_u_match_list *m_l;
       struct ebt_u_watcher_list *w_l;
       struct ebt_u_entries *entries = ebt_to_chain(replace);
       struct ebt_cntchanges *cc, *new_cc;

       if (rule_nr <= 0)
               rule_nr += entries->nentries;
       else
               rule_nr--;
       if (rule_nr > entries->nentries || rule_nr < 0) {
               ebt_print_error("The specified rule number is incorrect");
               return;
       }
       /* Go to the right position in the chain */
       if (rule_nr == entries->nentries)
               u_e = entries->entries;
       else {
               u_e = entries->entries->next;
               for (i = 0; i < rule_nr; i++)
                       u_e = u_e->next;
       }
       /* We're adding one rule */
       replace->nentries++;
       entries->nentries++;
       /* Insert the rule */
       new_entry->next = u_e;
       new_entry->prev = u_e->prev;
       u_e->prev->next = new_entry;
       u_e->prev = new_entry;
       new_cc = (struct ebt_cntchanges *)malloc(sizeof(struct ebt_cntchanges));
       if (!new_cc)
               ebt_print_memory();
       new_cc->type = CNT_ADD;
       new_cc->change = 0;
       if (new_entry->next == entries->entries) {
               for (i = replace->selected_chain+1; i < replace->num_chains; i++)
                       if (!replace->chains[i] || replace->chains[i]->nentries == 0)
                               continue;
                       else
                               break;
               if (i == replace->num_chains)
                       cc = replace->cc;
               else
                       cc = replace->chains[i]->entries->next->cc;
       } else
               cc = new_entry->next->cc;
       new_cc->next = cc;
       new_cc->prev = cc->prev;
       cc->prev->next = new_cc;
       cc->prev = new_cc;
       new_entry->cc = new_cc;

       /* Put the ebt_{match, watcher, target} pointers in place */
       m_l = new_entry->m_list;
       while (m_l) {
               m_l->m = ((struct ebt_u_match *)m_l->m)->m;
               m_l = m_l->next;
       }
       w_l = new_entry->w_list;
       while (w_l) {
               w_l->w = ((struct ebt_u_watcher *)w_l->w)->w;
               w_l = w_l->next;
       }
       new_entry->t = ((struct ebt_u_target *)new_entry->t)->t;
       /* Update the counter_offset of chains behind this one */
       for (i = replace->selected_chain+1; i < replace->num_chains; i++) {
               entries = replace->chains[i];
               if (!(entries = replace->chains[i]))
                       continue;
               entries->counter_offset++;
       }
}

/* If *begin==*end==0 then find the rule corresponding to new_entry,
 * else make the rule numbers positive (starting from 0) and check
 * for bad rule numbers. */
static int check_and_change_rule_number(struct ebt_u_replace *replace,
   struct ebt_u_entry *new_entry, int *begin, int *end)
{
       struct ebt_u_entries *entries = ebt_to_chain(replace);

       if (*begin < 0)
               *begin += entries->nentries + 1;
       if (*end < 0)
               *end += entries->nentries + 1;

       if (*begin < 0 || *begin > *end || *end > entries->nentries) {
               ebt_print_error("Sorry, wrong rule numbers");
               return -1;
       }

       if ((*begin * *end == 0) && (*begin + *end != 0))
               ebt_print_bug("begin and end should be either both zero, "
                             "either both non-zero");
       if (*begin != 0) {
               (*begin)--;
               (*end)--;
       } else {
               *begin = ebt_check_rule_exists(replace, new_entry);
               *end = *begin;
               if (*begin == -1) {
                       ebt_print_error("Sorry, rule does not exist");
                       return -1;
               }
       }
       return 0;
}

/* Delete a rule or rules
 * begin == end == 0: delete the rule corresponding to new_entry
 *
 * The first rule has rule nr 1, the last rule has rule nr -1, etc.
 * This function expects the ebt_{match,watcher,target} members of new_entry
 * to contain pointers to ebt_u_{match,watcher,target}. */
void ebt_delete_rule(struct ebt_u_replace *replace,
                    struct ebt_u_entry *new_entry, int begin, int end)
{
       int i,  nr_deletes;
       struct ebt_u_entry *u_e, *u_e2, *u_e3;
       struct ebt_u_entries *entries = ebt_to_chain(replace);

       if (check_and_change_rule_number(replace, new_entry, &begin, &end))
               return;
       /* We're deleting rules */
       nr_deletes = end - begin + 1;
       replace->nentries -= nr_deletes;
       entries->nentries -= nr_deletes;
       /* Go to the right position in the chain */
       u_e = entries->entries->next;
       for (i = 0; i < begin; i++)
               u_e = u_e->next;
       u_e3 = u_e->prev;
       /* Remove the rules */
       for (i = 0; i < nr_deletes; i++) {
               u_e2 = u_e;
               ebt_delete_cc(u_e2->cc);
               u_e = u_e->next;
               /* Free everything */
               ebt_free_u_entry(u_e2);
               free(u_e2);
       }
       u_e3->next = u_e;
       u_e->prev = u_e3;
       /* Update the counter_offset of chains behind this one */
       for (i = replace->selected_chain+1; i < replace->num_chains; i++) {
               if (!(entries = replace->chains[i]))
                       continue;
               entries->counter_offset -= nr_deletes;
       }
}

/* Change the counters of a rule or rules
 * begin == end == 0: change counters of the rule corresponding to new_entry
 *
 * The first rule has rule nr 1, the last rule has rule nr -1, etc.
 * This function expects the ebt_{match,watcher,target} members of new_entry
 * to contain pointers to ebt_u_{match,watcher,target}.
 * The mask denotes the following:
 *    pcnt: mask % 3 = 0 : change; = 1: increment; = 2: decrement
 *    bcnt: mask / 3 = 0 : change; = 1: increment = 2: increment
 * In daemon mode, mask==0 must hold */
void ebt_change_counters(struct ebt_u_replace *replace,
                    struct ebt_u_entry *new_entry, int begin, int end,
                    struct ebt_counter *cnt, int mask)
{
       int i;
       struct ebt_u_entry *u_e;
       struct ebt_u_entries *entries = ebt_to_chain(replace);

       if (check_and_change_rule_number(replace, new_entry, &begin, &end))
               return;
       u_e = entries->entries->next;
       for (i = 0; i < begin; i++)
               u_e = u_e->next;
       for (i = end-begin+1; i > 0; i--) {
               if (mask % 3 == 0) {
                       u_e->cnt.pcnt = (*cnt).pcnt;
                       u_e->cnt_surplus.pcnt = 0;
               } else {
#ifdef EBT_DEBUG
                       if (u_e->cc->type != CNT_NORM)
                               ebt_print_bug("cc->type != CNT_NORM");
#endif
                       u_e->cnt_surplus.pcnt = (*cnt).pcnt;
               }

               if (mask / 3 == 0) {
                       u_e->cnt.bcnt = (*cnt).bcnt;
                       u_e->cnt_surplus.bcnt = 0;
               } else {
#ifdef EBT_DEBUG
                       if (u_e->cc->type != CNT_NORM)
                               ebt_print_bug("cc->type != CNT_NORM");
#endif
                       u_e->cnt_surplus.bcnt = (*cnt).bcnt;
               }
               if (u_e->cc->type != CNT_ADD)
                       u_e->cc->type = CNT_CHANGE;
               u_e->cc->change = mask;
               u_e = u_e->next;
       }
}

/* If selected_chain == -1 then zero all counters,
 * otherwise, zero the counters of selected_chain */
void ebt_zero_counters(struct ebt_u_replace *replace)
{
       struct ebt_u_entries *entries = ebt_to_chain(replace);
       struct ebt_u_entry *next;
       int i;

       if (!entries) {
               for (i = 0; i < replace->num_chains; i++) {
                       if (!(entries = replace->chains[i]))
                               continue;
                       next = entries->entries->next;
                       while (next != entries->entries) {
                               if (next->cc->type == CNT_NORM)
                                       next->cc->type = CNT_CHANGE;
                               next->cnt.bcnt = next->cnt.pcnt = 0;
                               next->cc->change = 0;
                               next = next->next;
                       }
               }
       } else {
               if (entries->nentries == 0)
                       return;

               next = entries->entries->next;
               while (next != entries->entries) {
                       if (next->cc->type == CNT_NORM)
                               next->cc->type = CNT_CHANGE;
                       next->cnt.bcnt = next->cnt.pcnt = 0;
                       next = next->next;
               }
       }
}

/* Add a new chain and specify its policy */
void ebt_new_chain(struct ebt_u_replace *replace, const char *name, int policy)
{
       struct ebt_u_entries *new;

       if (replace->num_chains == replace->max_chains)
               ebt_double_chains(replace);
       new = (struct ebt_u_entries *)malloc(sizeof(struct ebt_u_entries));
       if (!new)
               ebt_print_memory();
       replace->chains[replace->num_chains++] = new;
       new->nentries = 0;
       new->policy = policy;
       new->counter_offset = replace->nentries;
       new->hook_mask = 0;
       strcpy(new->name, name);
       new->entries = (struct ebt_u_entry *)malloc(sizeof(struct ebt_u_entry));
       if (!new->entries)
               ebt_print_memory();
       new->entries->next = new->entries->prev = new->entries;
       new->kernel_start = NULL;
}

/* returns -1 if the chain is referenced, 0 on success */
static int ebt_delete_a_chain(struct ebt_u_replace *replace, int chain, int print_err)
{
       int tmp = replace->selected_chain;
       /* If the chain is referenced, don't delete it,
        * also decrement jumps to a chain behind the
        * one we're deleting */
       replace->selected_chain = chain;
       if (ebt_check_for_references(replace, print_err))
               return -1;
       decrease_chain_jumps(replace);
       ebt_flush_chains(replace);
       replace->selected_chain = tmp;
       free(replace->chains[chain]->entries);
       free(replace->chains[chain]);
       memmove(replace->chains+chain, replace->chains+chain+1, (replace->num_chains-chain-1)*sizeof(void *));
       replace->num_chains--;
       return 0;
}

/* Selected_chain == -1: delete all non-referenced udc
 * selected_chain < NF_BR_NUMHOOKS is illegal */
void ebt_delete_chain(struct ebt_u_replace *replace)
{
       if (replace->selected_chain != -1 && replace->selected_chain < NF_BR_NUMHOOKS)
               ebt_print_bug("You can't remove a standard chain");
       if (replace->selected_chain == -1) {
               int i = NF_BR_NUMHOOKS;

               while (i < replace->num_chains)
                       if (ebt_delete_a_chain(replace, i, 0))
                               i++;
       } else
               ebt_delete_a_chain(replace, replace->selected_chain, 1);
}

/* Rename an existing chain. */
void ebt_rename_chain(struct ebt_u_replace *replace, const char *name)
{
       struct ebt_u_entries *entries = ebt_to_chain(replace);

       if (!entries)
               ebt_print_bug("ebt_rename_chain: entries == NULL");
       strcpy(entries->name, name);
}


           /*
*************************
*************************
**SPECIALIZED*FUNCTIONS**
*************************
*************************
            */


void ebt_double_chains(struct ebt_u_replace *replace)
{
       struct ebt_u_entries **new;

       replace->max_chains *= 2;
       new = (struct ebt_u_entries **)malloc(replace->max_chains*sizeof(void *));
       if (!new)
               ebt_print_memory();
       memcpy(new, replace->chains, replace->max_chains/2*sizeof(void *));
       free(replace->chains);
       replace->chains = new;
}

/* Executes the final_check() function for all extensions used by the rule
 * ebt_check_for_loops should have been executed earlier, to make sure the
 * hook_mask is correct. The time argument to final_check() is set to 1,
 * meaning it's the second time the final_check() function is executed. */
void ebt_do_final_checks(struct ebt_u_replace *replace, struct ebt_u_entry *e,
                        struct ebt_u_entries *entries)
{
       struct ebt_u_match_list *m_l;
       struct ebt_u_watcher_list *w_l;
       struct ebt_u_target *t;
       struct ebt_u_match *m;
       struct ebt_u_watcher *w;

       m_l = e->m_list;
       w_l = e->w_list;
       while (m_l) {
               m = ebt_find_match(m_l->m->u.name);
               m->final_check(e, m_l->m, replace->name,
                  entries->hook_mask, 1);
               if (ebt_errormsg[0] != '\0')
                       return;
               m_l = m_l->next;
       }
       while (w_l) {
               w = ebt_find_watcher(w_l->w->u.name);
               w->final_check(e, w_l->w, replace->name,
                  entries->hook_mask, 1);
               if (ebt_errormsg[0] != '\0')
                       return;
               w_l = w_l->next;
       }
       t = ebt_find_target(e->t->u.name);
       t->final_check(e, e->t, replace->name,
          entries->hook_mask, 1);
}

/* Returns 1 (if it returns) when the chain is referenced, 0 when it isn't.
 * print_err: 0 (resp. 1) = don't (resp. do) print error when referenced */
int ebt_check_for_references(struct ebt_u_replace *replace, int print_err)
{
       if (print_err)
               return iterate_entries(replace, 1);
       else
               return iterate_entries(replace, 2);
}

/* chain_nr: nr of the udc (>= NF_BR_NUMHOOKS)
 * Returns 1 (if it returns) when the chain is referenced, 0 when it isn't.
 * print_err: 0 (resp. 1) = don't (resp. do) print error when referenced */
int ebt_check_for_references2(struct ebt_u_replace *replace, int chain_nr,
                              int print_err)
{
       int tmp = replace->selected_chain, ret;

       replace->selected_chain = chain_nr;
       if (print_err)
               ret = iterate_entries(replace, 1);
       else
               ret = iterate_entries(replace, 2);
       replace->selected_chain = tmp;
       return ret;
}

struct ebt_u_stack
{
       int chain_nr;
       int n;
       struct ebt_u_entry *e;
       struct ebt_u_entries *entries;
};

/* Checks for loops
 * As a by-product, the hook_mask member of each chain is filled in
 * correctly. The check functions of the extensions need this hook_mask
 * to know from which standard chains they can be called. */
void ebt_check_for_loops(struct ebt_u_replace *replace)
{
       int chain_nr , i, j , k, sp = 0, verdict;
       struct ebt_u_entries *entries, *entries2;
       struct ebt_u_stack *stack = NULL;
       struct ebt_u_entry *e;

       /* Initialize hook_mask to 0 */
       for (i = 0; i < replace->num_chains; i++) {
               if (!(entries = replace->chains[i]))
                       continue;
               if (i < NF_BR_NUMHOOKS)
                       /* (1 << NF_BR_NUMHOOKS) implies it's a standard chain
                        * (usefull in the final_check() funtions) */
                       entries->hook_mask = (1 << i) | (1 << NF_BR_NUMHOOKS);
               else
                       entries->hook_mask = 0;
       }
       if (replace->num_chains == NF_BR_NUMHOOKS)
               return;
       stack = (struct ebt_u_stack *)malloc((replace->num_chains - NF_BR_NUMHOOKS) * sizeof(struct ebt_u_stack));
       if (!stack)
               ebt_print_memory();

       /* Check for loops, starting from every base chain */
       for (i = 0; i < NF_BR_NUMHOOKS; i++) {
               if (!(entries = replace->chains[i]))
                       continue;
               chain_nr = i;

               e = entries->entries->next;
               for (j = 0; j < entries->nentries; j++) {
                       if (strcmp(e->t->u.name, EBT_STANDARD_TARGET))
                               goto letscontinue;
                       verdict = ((struct ebt_standard_target *)(e->t))->verdict;
                       if (verdict < 0)
                               goto letscontinue;
                       /* Now see if we've been here before */
                       for (k = 0; k < sp; k++)
                               if (stack[k].chain_nr == verdict + NF_BR_NUMHOOKS) {
                                       ebt_print_error("Loop from chain '%s' to chain '%s'",
                                          replace->chains[chain_nr]->name,
                                          replace->chains[stack[k].chain_nr]->name);
                                       goto free_stack;
                               }
                       entries2 = replace->chains[verdict + NF_BR_NUMHOOKS];
                       /* check if we've dealt with this chain already */
                       if (entries2->hook_mask & (1<<i))
                               goto letscontinue;
                       entries2->hook_mask |= entries->hook_mask;
                       /* Jump to the chain, make sure we know how to get back */
                       stack[sp].chain_nr = chain_nr;
                       stack[sp].n = j;
                       stack[sp].entries = entries;
                       stack[sp].e = e;
                       sp++;
                       j = -1;
                       e = entries2->entries->next;
                       chain_nr = verdict + NF_BR_NUMHOOKS;
                       entries = entries2;
                       continue;
letscontinue:
                       e = e->next;
               }
               /* We are at the end of a standard chain */
               if (sp == 0)
                       continue;
               /* Go back to the chain one level higher */
               sp--;
               j = stack[sp].n;
               chain_nr = stack[sp].chain_nr;
               e = stack[sp].e;
               entries = stack[sp].entries;
               goto letscontinue;
       }
free_stack:
       free(stack);
       return;
}

/* The user will use the match, so put it in new_entry. The ebt_u_match
 * pointer is put in the ebt_entry_match pointer. ebt_add_rule will
 * fill in the final value for new->m. Unless the rule is added to a chain,
 * the pointer will keep pointing to the ebt_u_match (until the new_entry
 * is freed). I know, I should use a union for these 2 pointer types... */
void ebt_add_match(struct ebt_u_entry *new_entry, struct ebt_u_match *m)
{
       struct ebt_u_match_list **m_list, *new;

       for (m_list = &new_entry->m_list; *m_list; m_list = &(*m_list)->next);
       new = (struct ebt_u_match_list *)
          malloc(sizeof(struct ebt_u_match_list));
       if (!new)
               ebt_print_memory();
       *m_list = new;
       new->next = NULL;
       new->m = (struct ebt_entry_match *)m;
}

void ebt_add_watcher(struct ebt_u_entry *new_entry, struct ebt_u_watcher *w)
{
       struct ebt_u_watcher_list **w_list;
       struct ebt_u_watcher_list *new;

       for (w_list = &new_entry->w_list; *w_list; w_list = &(*w_list)->next);
       new = (struct ebt_u_watcher_list *)
          malloc(sizeof(struct ebt_u_watcher_list));
       if (!new)
               ebt_print_memory();
       *w_list = new;
       new->next = NULL;
       new->w = (struct ebt_entry_watcher *)w;
}


        /*
*******************
*******************
**OTHER*FUNCTIONS**
*******************
*******************
         */


/* type = 0 => update chain jumps
 * type = 1 => check for reference, print error when referenced
 * type = 2 => check for reference, don't print error when referenced
 *
 * Returns 1 when type == 1 and the chain is referenced
 * returns 0 otherwise */
static int iterate_entries(struct ebt_u_replace *replace, int type)
{
       int i, j, chain_nr = replace->selected_chain - NF_BR_NUMHOOKS;
       struct ebt_u_entries *entries;
       struct ebt_u_entry *e;

       if (chain_nr < 0)
               ebt_print_bug("iterate_entries: udc = %d < 0", chain_nr);
       for (i = 0; i < replace->num_chains; i++) {
               if (!(entries = replace->chains[i]))
                       continue;
               e = entries->entries->next;
               for (j = 0; j < entries->nentries; j++) {
                       int chain_jmp;

                       if (strcmp(e->t->u.name, EBT_STANDARD_TARGET)) {
                               e = e->next;
                               continue;
                       }
                       chain_jmp = ((struct ebt_standard_target *)e->t)->
                                   verdict;
                       switch (type) {
                       case 1:
                       case 2:
                       if (chain_jmp == chain_nr) {
                               if (type == 2)
                                       return 1;
                               ebt_print_error("Can't delete the chain '%s', it's referenced in chain '%s', rule %d",
                                               replace->chains[chain_nr + NF_BR_NUMHOOKS]->name, entries->name, j);
                               return 1;
                       }
                       break;
                       case 0:
                       /* Adjust the chain jumps when necessary */
                       if (chain_jmp > chain_nr)
                               ((struct ebt_standard_target *)e->t)->verdict--;
                       break;
                       } /* End switch */
                       e = e->next;
               }
       }
       return 0;
}

static void decrease_chain_jumps(struct ebt_u_replace *replace)
{
       iterate_entries(replace, 0);
}

/* Used in initialization code of modules */
void ebt_register_match(struct ebt_u_match *m)
{
       int size = EBT_ALIGN(m->size) + sizeof(struct ebt_entry_match);
       struct ebt_u_match **i;

       m->m = (struct ebt_entry_match *)malloc(size);
       if (!m->m)
               ebt_print_memory();
       strcpy(m->m->u.name, m->name);
       m->m->match_size = EBT_ALIGN(m->size);
       m->init(m->m);

       for (i = &ebt_matches; *i; i = &((*i)->next));
       m->next = NULL;
       *i = m;
}

void ebt_register_watcher(struct ebt_u_watcher *w)
{
       int size = EBT_ALIGN(w->size) + sizeof(struct ebt_entry_watcher);
       struct ebt_u_watcher **i;

       w->w = (struct ebt_entry_watcher *)malloc(size);
       if (!w->w)
               ebt_print_memory();
       strcpy(w->w->u.name, w->name);
       w->w->watcher_size = EBT_ALIGN(w->size);
       w->init(w->w);

       for (i = &ebt_watchers; *i; i = &((*i)->next));
       w->next = NULL;
       *i = w;
}

void ebt_register_target(struct ebt_u_target *t)
{
       int size = EBT_ALIGN(t->size) + sizeof(struct ebt_entry_target);
       struct ebt_u_target **i;

       t->t = (struct ebt_entry_target *)malloc(size);
       if (!t->t)
               ebt_print_memory();
       strcpy(t->t->u.name, t->name);
       t->t->target_size = EBT_ALIGN(t->size);
       t->init(t->t);

       for (i = &ebt_targets; *i; i = &((*i)->next));
       t->next = NULL;
       *i = t;
}

void ebt_register_table(struct ebt_u_table *t)
{
       t->next = ebt_tables;
       ebt_tables = t;
}

void ebt_iterate_matches(void (*f)(struct ebt_u_match *))
{
       struct ebt_u_match *i;

       for (i = ebt_matches; i; i = i->next)
               f(i);
}

void ebt_iterate_watchers(void (*f)(struct ebt_u_watcher *))
{
       struct ebt_u_watcher *i;

       for (i = ebt_watchers; i; i = i->next)
               f(i);
}

void ebt_iterate_targets(void (*f)(struct ebt_u_target *))
{
       struct ebt_u_target *i;

       for (i = ebt_targets; i; i = i->next)
               f(i);
}

/* Don't use this function, use ebt_print_bug() */
void __ebt_print_bug(char *file, int line, char *format, ...)
{
       va_list l;

       va_start(l, format);
       fprintf(stderr, PROGNAME" v"PROGVERSION":%s:%d:--BUG--: \n", file, line);
       vfprintf(stderr, format, l);
       fprintf(stderr, "\n");
       va_end(l);
       exit (-1);
}

/* The error messages are put in here when ebt_silent == 1
 * ebt_errormsg[0] == '\0' implies there was no error */
char ebt_errormsg[ERRORMSG_MAXLEN];
/* When error messages should not be printed on the screen, after which
 * the program exit()s, set ebt_silent to 1. */
int ebt_silent;
/* Don't use this function, use ebt_print_error() */
void __ebt_print_error(char *format, ...)
{
       va_list l;

       va_start(l, format);
       if (ebt_silent && ebt_errormsg[0] == '\0') {
               vsnprintf(ebt_errormsg, ERRORMSG_MAXLEN, format, l);
               va_end(l);
       } else {
               vfprintf(stderr, format, l);
               fprintf(stderr, ".\n");
               va_end(l);
               exit (-1);
       }
}