net/9p: add privport option to 9p tcp transport
[linux-drm-fsl-dcu.git] / net / 9p / trans_fd.c
index 02efb25c295705141b3c477a2712864674ae42cc..3ffda1b3799b54340a0ba7851fa0f1c3cea6710c 100644 (file)
@@ -63,6 +63,7 @@ struct p9_fd_opts {
        int rfd;
        int wfd;
        u16 port;
+       int privport;
 };
 
 /**
@@ -87,12 +88,15 @@ struct p9_trans_fd {
 enum {
        /* Options that take integer arguments */
        Opt_port, Opt_rfdno, Opt_wfdno, Opt_err,
+       /* Options that take no arguments */
+       Opt_privport,
 };
 
 static const match_table_t tokens = {
        {Opt_port, "port=%u"},
        {Opt_rfdno, "rfdno=%u"},
        {Opt_wfdno, "wfdno=%u"},
+       {Opt_privport, "privport"},
        {Opt_err, NULL},
 };
 
@@ -161,6 +165,9 @@ static DEFINE_SPINLOCK(p9_poll_lock);
 static LIST_HEAD(p9_poll_pending_list);
 static DECLARE_WORK(p9_poll_work, p9_poll_workfn);
 
+static unsigned int p9_ipport_resv_min = P9_DEF_MIN_RESVPORT;
+static unsigned int p9_ipport_resv_max = P9_DEF_MAX_RESVPORT;
+
 static void p9_mux_poll_stop(struct p9_conn *m)
 {
        unsigned long flags;
@@ -741,7 +748,7 @@ static int parse_opts(char *params, struct p9_fd_opts *opts)
                if (!*p)
                        continue;
                token = match_token(p, tokens, args);
-               if (token != Opt_err) {
+               if ((token != Opt_err) && (token != Opt_privport)) {
                        r = match_int(&args[0], &option);
                        if (r < 0) {
                                p9_debug(P9_DEBUG_ERROR,
@@ -759,6 +766,9 @@ static int parse_opts(char *params, struct p9_fd_opts *opts)
                case Opt_wfdno:
                        opts->wfd = option;
                        break;
+               case Opt_privport:
+                       opts->privport = 1;
+                       break;
                default:
                        continue;
                }
@@ -898,6 +908,24 @@ static inline int valid_ipaddr4(const char *buf)
        return 0;
 }
 
+static int p9_bind_privport(struct socket *sock)
+{
+       struct sockaddr_in cl;
+       int port, err = -EINVAL;
+
+       memset(&cl, 0, sizeof(cl));
+       cl.sin_family = AF_INET;
+       cl.sin_addr.s_addr = INADDR_ANY;
+       for (port = p9_ipport_resv_max; port >= p9_ipport_resv_min; port--) {
+               cl.sin_port = htons((ushort)port);
+               err = kernel_bind(sock, (struct sockaddr *)&cl, sizeof(cl));
+               if (err != -EADDRINUSE)
+                       break;
+       }
+       return err;
+}
+
+
 static int
 p9_fd_create_tcp(struct p9_client *client, const char *addr, char *args)
 {
@@ -926,6 +954,16 @@ p9_fd_create_tcp(struct p9_client *client, const char *addr, char *args)
                return err;
        }
 
+       if (opts.privport) {
+               err = p9_bind_privport(csocket);
+               if (err < 0) {
+                       pr_err("%s (%d): problem binding to privport\n",
+                              __func__, task_pid_nr(current));
+                       sock_release(csocket);
+                       return err;
+               }
+       }
+
        err = csocket->ops->connect(csocket,
                                    (struct sockaddr *)&sin_server,
                                    sizeof(struct sockaddr_in), 0);