/*
ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010 Giovanni Di Sirio.
This file is part of ChibiOS/RT.
ChibiOS/RT is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
ChibiOS/RT 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
* There are several kind of queues:
* - Input queue, unidirectional queue where the writer is the
* lower side and the reader is the upper side.
* - Output queue, unidirectional queue where the writer is the
* upper side and the reader is the lower side.
* - Full duplex queue, bidirectional queue. Full duplex queues
* are implemented by pairing an input queue and an output queue
* together.
* .
* In order to use the I/O queues the @p CH_USE_QUEUES option must
* be enabled in @p chconf.h.
* I/O queues are usually used as an implementation layer for the I/O
* channels interface, also see @ref io_channels.
* @{
*/
#include "ch.h"
#if CH_USE_QUEUES
/**
* @brief Initializes an input queue.
* @details A Semaphore is internally initialized and works as a counter of
* the bytes contained in the queue.
* @note The callback is invoked from within the S-Locked system state,
* see @ref system_states.
*
* @param[out] iqp pointer to an @p InputQueue structure
* @param[in] bp pointer to a memory area allocated as queue buffer
* @param[in] size size of the queue buffer
* @param[in] infy pointer to a callback function that is invoked when
* data is read from the queue. The value can be @p NULL.
*/
void chIQInit(InputQueue *iqp, uint8_t *bp, size_t size, qnotify_t infy) {
iqp->q_buffer = iqp->q_rdptr = iqp->q_wrptr = bp;
iqp->q_top = bp + size;
iqp->q_notify = infy;
chSemInit(&iqp->q_sem, 0);
}
/**
* @brief Resets an input queue.
* @details All the data in the input queue is erased and lost, any waiting
* thread is resumed with status @p Q_RESET.
* @note A reset operation can be used by a low level driver in order to
* obtain immediate attention from the high level layers.
*
* @param[in] iqp pointer to an @p InputQueue structure
*/
void chIQResetI(InputQueue *iqp) {
iqp->q_rdptr = iqp->q_wrptr = iqp->q_buffer;
chSemResetI(&iqp->q_sem, 0);
}
/**
* @brief Input queue write.
* @details A byte value is written into the low end of an input queue.
*
* @param[in] iqp pointer to an @p InputQueue structure
* @param[in] b the byte value to be written in the queue
* @return The operation status, it can be one of:
* @retval Q_OK if the operation has been completed with success.
* @retval Q_FULL if the queue is full and the operation cannot be
* completed.
*/
msg_t chIQPutI(InputQueue *iqp, uint8_t b) {
if (chIQIsFull(iqp))
return Q_FULL;
*iqp->q_wrptr++ = b;
if (iqp->q_wrptr >= iqp->q_top)
iqp->q_wrptr = iqp->q_buffer;
chSemSignalI(&iqp->q_sem);
return Q_OK;
}
/**
* @brief Input queue read with timeout.
* @details This function reads a byte value from an input queue. If the queue
* is empty then the calling thread is suspended until a byte arrives
* in the queue or a timeout occurs.
*
* @param[in] iqp pointer to an @p InputQueue structure
* @param[in] time the number of ticks before the operation timeouts,
* the following special values are allowed:
* - @a TIME_IMMEDIATE immediate timeout.
* - @a TIME_INFINITE no timeout.
* .
* @return A byte value from the queue or:
* @retval Q_TIMEOUT if the specified time expired.
* @retval Q_RESET if the queue was reset.
*/
msg_t chIQGetTimeout(InputQueue *iqp, systime_t time) {
uint8_t b;
msg_t msg;
chSysLock();
if ((msg = chSemWaitTimeoutS(&iqp->q_sem, time)) < RDY_OK) {
chSysUnlock();
return msg;
}
b = *iqp->q_rdptr++;
if (iqp->q_rdptr >= iqp->q_top)
iqp->q_rdptr = iqp->q_buffer;
if (iqp->q_notify)
iqp->q_notify();
chSysUnlock();
return b;
}
/**
* @brief Input queue read with timeout.
* @details The function reads data from an input queue into a buffer. The
* operation completes when the specified amount of data has been
* transferred or after the specified timeout or if the queue has
* been reset.
* @note The function is not atomic, if you need atomicity it is suggested
* to use a semaphore or a mutex for mutual exclusion.
* @note The queue callback is invoked before entering a sleep state and at
* the end of the transfer.
*
* @param[in] iqp pointer to an @p InputQueue structure
* @param[out] bp pointer to the data buffer
* @param[in] n the maximum amount of data to be transferred
* @param[in] time the number of ticks before the operation timeouts,
* the following special values are allowed:
* - @a TIME_IMMEDIATE immediate timeout.
* - @a TIME_INFINITE no timeout.
* .
* @return The number of bytes effectively transferred.
*/
size_t chIQReadTimeout(InputQueue *iqp, uint8_t *bp,
size_t n, systime_t time) {
qnotify_t nfy = iqp->q_notify;
size_t r = 0;
chSysLock();
while (TRUE) {
if (chIQIsEmpty(iqp)) {
if (nfy)
nfy();
if ((chSemWaitTimeoutS(&iqp->q_sem, time) != RDY_OK)) {
chSysUnlock();
return r;
}
}
else
chSemFastWaitI(&iqp->q_sem);
*bp++ = *iqp->q_rdptr++;
if (iqp->q_rdptr >= iqp->q_top)
iqp->q_rdptr = iqp->q_buffer;
if (nfy)
nfy();
chSysUnlock(); /* Gives a preemption chance in a controlled point.*/
r++;
if (--n == 0) {
chSysLock();
if (nfy)
nfy();
chSysUnlock();
return r;
}
chSysLock();
}
}
/**
* @brief Initializes an output queue.
* @details A Semaphore is internally initialized and works as a counter of
* the free bytes in the queue.
* @note The callback is invoked from within the S-Locked system state,
* see @ref system_states.
*
* @param[out] oqp pointer to an @p OutputQueue structure
* @param[in] bp pointer to a memory area allocated as queue buffer
* @param[in] size size of the queue buffer
* @param[in] onfy pointer to a callback function that is invoked when
* data is written to the queue. The value can be @p NULL.
*/
void chOQInit(OutputQueue *oqp, uint8_t *bp, size_t size, qnotify_t onfy) {
oqp->q_buffer = oqp->q_rdptr = oqp->q_wrptr = bp;
oqp->q_top = bp + size;
oqp->q_notify = onfy;
chSemInit(&oqp->q_sem, size);
}
/**
* @brief Resets an output queue.
* @details All the data in the output queue is erased and lost, any waiting
* thread is resumed with status @p Q_RESET.
* @note A reset operation can be used by a low level driver in order to
* obtain immediate attention from the high level layers.
*
* @param[in] oqp pointer to an @p OutputQueue structure
*/
void chOQResetI(OutputQueue *oqp) {
oqp->q_rdptr = oqp->q_wrptr = oqp->q_buffer;
chSemResetI(&oqp->q_sem, (cnt_t)(oqp->q_top - oqp->q_buffer));
}
/**
* @brief Output queue write with timeout.
* @details This function writes a byte value to an output queue. If the queue
* is full then the calling thread is suspended until there is space
* in the queue or a timeout occurs.
*
* @param[in] oqp pointer to an @p OutputQueue structure
* @param[in] b the byte value to be written in the queue
* @param[in] time the number of ticks before the operation timeouts,
* the following special values are allowed:
* - @a TIME_IMMEDIATE immediate timeout.
* - @a TIME_INFINITE no timeout.
* .
* @return The operation status:
* @retval Q_OK if the operation succeeded.
* @retval Q_TIMEOUT if the specified time expired.
* @retval Q_RESET if the queue was reset.
*/
msg_t chOQPutTimeout(OutputQueue *oqp, uint8_t b, systime_t time) {
msg_t msg;
chSysLock();
if ((msg = chSemWaitTimeoutS(&oqp->q_sem, time)) < RDY_OK) {
chSysUnlock();
return msg;
}
*oqp->q_wrptr++ = b;
if (oqp->q_wrptr >= oqp->q_top)
oqp->q_wrptr = oqp->q_buffer;
if (oqp->q_notify)
oqp->q_notify();
chSysUnlock();
return Q_OK;
}
/**
* @brief Output queue read.
* @details A byte value is read from the low end of an output queue.
*
* @param[in] oqp pointer to an @p OutputQueue structure
* @return The byte value from the queue or:
* @retval Q_EMPTY if the queue is empty.
*/
msg_t chOQGetI(OutputQueue *oqp) {
uint8_t b;
if (chOQIsEmpty(oqp))
return Q_EMPTY;
b = *oqp->q_rdptr++;
if (oqp->q_rdptr >= oqp->q_top)
oqp->q_rdptr = oqp->q_buffer;
chSemSignalI(&oqp->q_sem);
return b;
}
/**
* @brief Output queue write with timeout.
* @details The function writes data from a buffer to an output queue. The
* operation completes when the specified amount of data has been
* transferred or after the specified timeout or if the queue has
* been reset.
* @note The function is not atomic, if you need atomicity it is suggested
* to use a semaphore or a mutex for mutual exclusion.
* @note The queue callback is invoked before entering a sleep state and at
* the end of the transfer.
*
* @param[in] oqp pointer to an @p OutputQueue structure
* @param[out] bp pointer to the data buffer
* @param[in] n the maximum amount of data to be transferred
* @param[in] time the number of ticks before the operation timeouts,
* the following special values are allowed:
* - @a TIME_IMMEDIATE immediate timeout.
* - @a TIME_INFINITE no timeout.
* .
* @return The number of bytes effectively transferred.
*/
size_t chOQWriteTimeout(OutputQueue *oqp, const uint8_t *bp,
size_t n, systime_t time) {
qnotify_t nfy = oqp->q_notify;
size_t w = 0;
chSysLock();
while (TRUE) {
if (chOQIsFull(oqp)) {
if (nfy)
nfy();
if ((chSemWaitTimeoutS(&oqp->q_sem, time) != RDY_OK)) {
chSysUnlock();
return w;
}
}
else
chSemFastWaitI(&oqp->q_sem);
*oqp->q_wrptr++ = *bp++;
if (oqp->q_wrptr >= oqp->q_top)
oqp->q_wrptr = oqp->q_buffer;
chSysUnlock(); /* Gives a preemption chance in a controlled point.*/
w++;
if (--n == 0) {
chSysLock();
if (nfy)
nfy();
chSysUnlock();
return w;
}
chSysLock();
}
}
#endif /* CH_USE_QUEUES */
/** @} */
0 } /* 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 */
From: Florian Westphal <fw@strlen.de>
Date: Wed, 6 Dec 2017 16:18:16 +0100
Subject: [PATCH] netfilter: meta: secpath support
replacement for iptables "-m policy --dir in --policy {ipsec,none}".
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
--- a/include/uapi/linux/netfilter/nf_tables.h
+++ b/include/uapi/linux/netfilter/nf_tables.h
@@ -777,6 +777,7 @@ enum nft_exthdr_attributes {
* @NFT_META_OIFGROUP: packet output interface group
* @NFT_META_CGROUP: socket control group (skb->sk->sk_classid)
* @NFT_META_PRANDOM: a 32bit pseudo-random number
+ * @NFT_META_SECPATH: boolean, secpath_exists (!!skb->sp)
*/
enum nft_meta_keys {
NFT_META_LEN,
@@ -804,6 +805,7 @@ enum nft_meta_keys {
NFT_META_OIFGROUP,
NFT_META_CGROUP,
NFT_META_PRANDOM,
+ NFT_META_SECPATH,
};
/**
--- a/net/netfilter/nft_meta.c
+++ b/net/netfilter/nft_meta.c
@@ -210,6 +210,11 @@ void nft_meta_get_eval(const struct nft_
*dest = prandom_u32_state(state);
break;
}
+#ifdef CONFIG_XFRM
+ case NFT_META_SECPATH:
+ nft_reg_store8(dest, !!skb->sp);
+ break;
+#endif
default:
WARN_ON(1);
goto err;
@@ -310,6 +315,11 @@ int nft_meta_get_init(const struct nft_c
prandom_init_once(&nft_prandom_state);
len = sizeof(u32);
break;
+#ifdef CONFIG_XFRM
+ case NFT_META_SECPATH:
+ len = sizeof(u8);
+ break;
+#endif
default:
return -EOPNOTSUPP;
}
@@ -320,6 +330,38 @@ int nft_meta_get_init(const struct nft_c
}
EXPORT_SYMBOL_GPL(nft_meta_get_init);
+static int nft_meta_get_validate(const struct nft_ctx *ctx,
+ const struct nft_expr *expr,
+ const struct nft_data **data)
+{
+#ifdef CONFIG_XFRM
+ const struct nft_meta *priv = nft_expr_priv(expr);
+ unsigned int hooks;
+
+ if (priv->key != NFT_META_SECPATH)
+ return 0;
+
+ switch (ctx->afi->family) {
+ case NFPROTO_NETDEV:
+ hooks = 1 << NF_NETDEV_INGRESS;
+ break;
+ case NFPROTO_IPV4:
+ case NFPROTO_IPV6:
+ case NFPROTO_INET:
+ hooks = (1 << NF_INET_PRE_ROUTING) |
+ (1 << NF_INET_LOCAL_IN) |
+ (1 << NF_INET_FORWARD);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return nft_chain_validate_hooks(ctx->chain, hooks);
+#else
+ return 0;
+#endif
+}
+
int nft_meta_set_validate(const struct nft_ctx *ctx,
const struct nft_expr *expr,
const struct nft_data **data)
@@ -436,6 +478,7 @@ static const struct nft_expr_ops nft_met
.eval = nft_meta_get_eval,
.init = nft_meta_get_init,
.dump = nft_meta_get_dump,
+ .validate = nft_meta_get_validate,
};
static const struct nft_expr_ops nft_meta_set_ops = {